xref: /reactos/sdk/lib/drivers/rxce/rxce.c (revision 595b846d)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2017 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:        See COPYING in the top level directory
21  * PROJECT:          ReactOS kernel
22  * FILE:             sdk/lib/drivers/rxce/rxce.c
23  * PURPOSE:          RXCE library
24  * PROGRAMMER:       Pierre Schweitzer (pierre@reactos.org)
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include <rx.h>
30 #include <pseh/pseh2.h>
31 #include <dfs.h>
32 
33 #define NDEBUG
34 #include <debug.h>
35 
36 VOID
37 RxAssert(
38     PVOID Assert,
39     PVOID File,
40     ULONG Line,
41     PVOID Message);
42 
43 VOID
44 NTAPI
45 RxCreateSrvCallCallBack(
46     IN OUT PMRX_SRVCALL_CALLBACK_CONTEXT Context);
47 
48 NTSTATUS
49 RxFinishSrvCallConstruction(
50     PMRX_SRVCALLDOWN_STRUCTURE Calldown);
51 
52 VOID
53 NTAPI
54 RxFinishSrvCallConstructionDispatcher(
55     IN PVOID Context);
56 
57 NTSTATUS
58 RxInsertWorkQueueItem(
59     PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
60     WORK_QUEUE_TYPE WorkQueueType,
61     PRX_WORK_QUEUE_ITEM WorkQueueItem);
62 
63 PVOID
64 RxNewMapUserBuffer(
65     PRX_CONTEXT RxContext);
66 
67 VOID
68 NTAPI
69 RxpDestroySrvCall(
70     IN PVOID Context);
71 
72 VOID
73 RxpDispatchChangeBufferingStateRequests(
74     PSRV_CALL SrvCall,
75     PSRV_OPEN SrvOpen,
76     PLIST_ENTRY DiscardedRequests);
77 
78 VOID
79 NTAPI
80 RxScavengerTimerRoutine(
81     PVOID Context);
82 
83 VOID
84 NTAPI
85 RxTimerDispatch(
86     _In_ struct _KDPC *Dpc,
87     _In_opt_ PVOID DeferredContext,
88     _In_opt_ PVOID SystemArgument1,
89     _In_opt_ PVOID SystemArgument2);
90 
91 VOID
92 NTAPI
93 RxWorkItemDispatcher(
94     PVOID Context);
95 
96 PVOID
97 NTAPI
98 _RxAllocatePoolWithTag(
99     _In_ POOL_TYPE PoolType,
100     _In_ SIZE_T NumberOfBytes,
101     _In_ ULONG Tag);
102 
103 VOID
104 NTAPI
105 _RxFreePool(
106     _In_ PVOID Buffer);
107 
108 VOID
109 NTAPI
110 _RxFreePoolWithTag(
111     _In_ PVOID Buffer,
112     _In_ ULONG Tag);
113 
114 extern ULONG ReadAheadGranularity;
115 
116 volatile LONG RxNumberOfActiveFcbs = 0;
117 ULONG SerialNumber = 1;
118 PVOID RxNull =  NULL;
119 volatile ULONG RxContextSerialNumberCounter;
120 BOOLEAN RxStopOnLoudCompletion = TRUE;
121 BOOLEAN RxSrvCallConstructionDispatcherActive = FALSE;
122 LIST_ENTRY RxSrvCalldownList;
123 RX_SPIN_LOCK RxStrucSupSpinLock;
124 #if 0
125 ULONG RdbssReferenceTracingValue = (RDBSS_REF_TRACK_SRVCALL | RDBSS_REF_TRACK_NETROOT |
126                                     RDBSS_REF_TRACK_VNETROOT | RDBSS_REF_TRACK_NETFOBX |
127                                     RDBSS_REF_TRACK_NETFCB | RDBSS_REF_TRACK_SRVOPEN |
128                                     RX_PRINT_REF_TRACKING);
129 #else
130 ULONG RdbssReferenceTracingValue = 0;
131 #endif
132 LARGE_INTEGER RxWorkQueueWaitInterval[RxMaximumWorkQueue];
133 LARGE_INTEGER RxSpinUpDispatcherWaitInterval;
134 RX_DISPATCHER RxDispatcher;
135 RX_WORK_QUEUE_DISPATCHER RxDispatcherWorkQueues;
136 FAST_MUTEX RxLowIoPagingIoSyncMutex;
137 BOOLEAN RxContinueFromAssert = TRUE;
138 ULONG RxExplodePoolTags = 1;
139 LARGE_INTEGER RxTimerInterval;
140 RX_SPIN_LOCK RxTimerLock;
141 LIST_ENTRY RxTimerQueueHead;
142 LIST_ENTRY RxRecurrentWorkItemsList;
143 KDPC RxTimerDpc;
144 KTIMER RxTimer;
145 ULONG RxTimerTickCount;
146 #if DBG
147 BOOLEAN DumpDispatchRoutine = TRUE;
148 #else
149 BOOLEAN DumpDispatchRoutine = FALSE;
150 #endif
151 
152 #if RDBSS_ASSERTS
153 #ifdef ASSERT
154 #undef ASSERT
155 #endif
156 
157 #define ASSERT(exp)                               \
158     if (!(exp))                                   \
159     {                                             \
160         RxAssert(#exp, __FILE__, __LINE__, NULL); \
161     }
162 #endif
163 
164 #if RX_POOL_WRAPPER
165 #undef RxAllocatePool
166 #undef RxAllocatePoolWithTag
167 #undef RxFreePool
168 
169 #define RxAllocatePool(P, S) _RxAllocatePoolWithTag(P, S, 0)
170 #define RxAllocatePoolWithTag _RxAllocatePoolWithTag
171 #define RxFreePool _RxFreePool
172 #define RxFreePoolWithTag _RxFreePoolWithTag
173 #endif
174 
175 /* FUNCTIONS ****************************************************************/
176 
177 /*
178  * @implemented
179  */
180 NTSTATUS
181 NTAPI
182 RxAcquireExclusiveFcbResourceInMRx(
183     _Inout_ PMRX_FCB Fcb)
184 {
185     return RxAcquireExclusiveFcb(NULL, (PFCB)Fcb);
186 }
187 
188 /*
189  * @implemented
190  */
191 BOOLEAN
192 NTAPI
193 RxAcquireFcbForLazyWrite(
194     PVOID Context,
195     BOOLEAN Wait)
196 {
197     PFCB Fcb;
198     BOOLEAN Ret;
199 
200     PAGED_CODE();
201 
202     Fcb = Context;
203     /* The received context is a FCB */
204     ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
205     ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
206     ASSERT(Fcb->Specific.Fcb.LazyWriteThread == NULL);
207 
208     /* Acquire the paging resource (shared) */
209     Ret = ExAcquireResourceSharedLite(Fcb->Header.PagingIoResource, Wait);
210     if (Ret)
211     {
212         /* Update tracker information */
213         Fcb->PagingIoResourceFile = __FILE__;
214         Fcb->PagingIoResourceLine = __LINE__;
215         /* Lazy writer thread is the current one */
216         Fcb->Specific.Fcb.LazyWriteThread = PsGetCurrentThread();
217 
218         /* There is no top level IRP */
219         ASSERT(RxIsThisTheTopLevelIrp(NULL));
220         /* Now, there will be! */
221         Ret = RxTryToBecomeTheTopLevelIrp(NULL, (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP,
222                                           Fcb->RxDeviceObject, TRUE);
223         /* In case of failure, release the lock and reset everything */
224         if (!Ret)
225         {
226             Fcb->PagingIoResourceFile = NULL;
227             Fcb->PagingIoResourceLine = 0;
228             ExReleaseResourceLite(Fcb->Header.PagingIoResource);
229             Fcb->Specific.Fcb.LazyWriteThread = NULL;
230         }
231     }
232 
233     return Ret;
234 }
235 
236 /*
237  * @implemented
238  */
239 BOOLEAN
240 NTAPI
241 RxAcquireFcbForReadAhead(
242     PVOID Context,
243     BOOLEAN Wait)
244 {
245     PFCB Fcb;
246     BOOLEAN Ret;
247 
248     PAGED_CODE();
249 
250     Fcb = Context;
251     /* The received context is a FCB */
252     ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
253     ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
254 
255     Ret = ExAcquireResourceSharedLite(Fcb->Header.Resource, Wait);
256     if (Ret)
257     {
258         /* There is no top level IRP */
259         ASSERT(RxIsThisTheTopLevelIrp(NULL));
260         /* Now, there will be! */
261         Ret = RxTryToBecomeTheTopLevelIrp(NULL, (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP,
262                                           Fcb->RxDeviceObject, TRUE);
263         /* In case of failure, release the lock and reset everything */
264         if (!Ret)
265         {
266             ExReleaseResourceLite(Fcb->Header.Resource);
267         }
268     }
269 
270     return Ret;
271 }
272 
273 VOID
274 NTAPI
275 RxAcquireFileForNtCreateSection(
276     PFILE_OBJECT FileObject)
277 {
278     UNIMPLEMENTED;
279 }
280 
281 NTSTATUS
282 NTAPI
283 RxAcquireForCcFlush(
284     PFILE_OBJECT FileObject,
285     PDEVICE_OBJECT DeviceObject)
286 {
287     UNIMPLEMENTED;
288     return STATUS_NOT_IMPLEMENTED;
289 }
290 
291 /*
292  * @implemented
293  */
294 VOID
295 RxAddVirtualNetRootToNetRoot(
296     PNET_ROOT NetRoot,
297     PV_NET_ROOT VNetRoot)
298 {
299     PAGED_CODE();
300 
301     DPRINT("RxAddVirtualNetRootToNetRoot(%p, %p)\n", NetRoot, VNetRoot);
302 
303     /* Insert in the VNetRoot list - make sure lock is held */
304     ASSERT(RxIsPrefixTableLockExclusive(NetRoot->SrvCall->RxDeviceObject->pRxNetNameTable));
305 
306     VNetRoot->pNetRoot = (PMRX_NET_ROOT)NetRoot;
307     ++NetRoot->NumberOfVirtualNetRoots;
308     InsertTailList(&NetRoot->VirtualNetRoots, &VNetRoot->NetRootListEntry);
309 }
310 
311 /*
312  * @implemented
313  */
314 PVOID
315 RxAllocateFcbObject(
316     PRDBSS_DEVICE_OBJECT RxDeviceObject,
317     NODE_TYPE_CODE NodeType,
318     POOL_TYPE PoolType,
319     ULONG NameSize,
320     PVOID AlreadyAllocatedObject)
321 {
322     PFCB Fcb;
323     PFOBX Fobx;
324     PSRV_OPEN SrvOpen;
325     PVOID Buffer, PAPNBuffer;
326     PNON_PAGED_FCB NonPagedFcb;
327     PMINIRDR_DISPATCH Dispatch;
328     ULONG NonPagedSize, FobxSize, SrvOpenSize, FcbSize;
329 
330     PAGED_CODE();
331 
332     Dispatch = RxDeviceObject->Dispatch;
333 
334     NonPagedSize = 0;
335     FobxSize = 0;
336     SrvOpenSize = 0;
337     FcbSize = 0;
338 
339     Fcb = NULL;
340     Fobx = NULL;
341     SrvOpen = NULL;
342     NonPagedFcb = NULL;
343     PAPNBuffer = NULL;
344 
345     /* If we ask for FOBX, just allocate FOBX and its extension if asked */
346     if (NodeType == RDBSS_NTC_FOBX)
347     {
348         FobxSize = sizeof(FOBX);
349         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
350         {
351             FobxSize += QuadAlign(Dispatch->MRxFobxSize);
352         }
353     }
354     /* If we ask for SRV_OPEN, also allocate the "internal" FOBX and the extensions if asked */
355     else if (NodeType == RDBSS_NTC_SRVOPEN || NodeType == RDBSS_NTC_INTERNAL_SRVOPEN)
356     {
357         SrvOpenSize = sizeof(SRV_OPEN);
358         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
359         {
360             SrvOpenSize += QuadAlign(Dispatch->MRxSrvOpenSize);
361         }
362 
363         FobxSize = sizeof(FOBX);
364         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
365         {
366             FobxSize += QuadAlign(Dispatch->MRxFobxSize);
367         }
368     }
369     /* Otherwise, we're asked to allocate a FCB */
370     else
371     {
372         /* So, allocate the FCB and its extension if asked */
373         FcbSize = sizeof(FCB);
374         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FCB_EXTENSION))
375         {
376             FcbSize += QuadAlign(Dispatch->MRxFcbSize);
377         }
378 
379         /* If we're asked to allocate from nonpaged, also allocate the NON_PAGED_FCB
380          * Otherwise, it will be allocated later on, specifically
381          */
382         if (PoolType == NonPagedPool)
383         {
384             NonPagedSize = sizeof(NON_PAGED_FCB);
385         }
386 
387         /* And if it's not for a rename operation also allcoate the internal SRV_OPEN and FOBX and their extensions */
388         if (NodeType != RDBSS_NTC_OPENTARGETDIR_FCB)
389         {
390             SrvOpenSize = sizeof(SRV_OPEN);
391             if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
392             {
393                 SrvOpenSize += QuadAlign(Dispatch->MRxSrvOpenSize);
394             }
395 
396             FobxSize = sizeof(FOBX);
397             if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
398             {
399                 FobxSize += QuadAlign(Dispatch->MRxFobxSize);
400             }
401         }
402     }
403 
404     /* If we already have a buffer, go ahead */
405     if (AlreadyAllocatedObject != NULL)
406     {
407         Buffer = AlreadyAllocatedObject;
408     }
409     /* Otherwise, allocate it */
410     else
411     {
412         Buffer = RxAllocatePoolWithTag(PoolType, NameSize + FcbSize + SrvOpenSize + FobxSize + NonPagedSize, RX_FCB_POOLTAG);
413         if (Buffer == NULL)
414         {
415             return NULL;
416         }
417     }
418 
419     /* Now, get the pointers - FOBX is easy */
420     if (NodeType == RDBSS_NTC_FOBX)
421     {
422         Fobx = Buffer;
423     }
424     /* SRV_OPEN first, FOBX next */
425     else if (NodeType == RDBSS_NTC_SRVOPEN)
426     {
427         SrvOpen = Buffer;
428         Fobx = Add2Ptr(Buffer, SrvOpenSize);
429     }
430     else if (NodeType == RDBSS_NTC_INTERNAL_SRVOPEN)
431     {
432         SrvOpen = Buffer;
433     }
434     else
435     {
436         /* FCB first, and if needed, SRV_OPEN next, FOBX last */
437         Fcb = Buffer;
438         if (NodeType != RDBSS_NTC_OPENTARGETDIR_FCB)
439         {
440             SrvOpen = Add2Ptr(Buffer, FcbSize);
441             Fobx = Add2Ptr(Buffer, FcbSize + SrvOpenSize);
442         }
443 
444         /* If we were not allocated from non paged, allocate the NON_PAGED_FCB now */
445         if (PoolType != NonPagedPool)
446         {
447             NonPagedFcb = RxAllocatePoolWithTag(NonPagedPool, sizeof(NON_PAGED_FCB), RX_NONPAGEDFCB_POOLTAG);
448             if (NonPagedFcb == NULL)
449             {
450                 RxFreePoolWithTag(Buffer, RX_FCB_POOLTAG);
451                 return NULL;
452             }
453 
454             PAPNBuffer = Add2Ptr(Buffer, FcbSize + SrvOpenSize + FobxSize);
455         }
456         /* Otherwise, just point at the right place in what has been allocated previously */
457         else
458         {
459             NonPagedFcb = Add2Ptr(Fobx, FobxSize);
460             PAPNBuffer = Add2Ptr(Fobx, FobxSize + NonPagedSize);
461         }
462     }
463 
464     /* If we have allocated a SRV_OPEN, initialize it */
465     if (SrvOpen != NULL)
466     {
467         ZeroAndInitializeNodeType(SrvOpen, RDBSS_NTC_SRVOPEN, SrvOpenSize);
468 
469         if (NodeType == RDBSS_NTC_SRVOPEN)
470         {
471             SrvOpen->InternalFobx = Fobx;
472         }
473         else
474         {
475             SrvOpen->InternalFobx = NULL;
476             SrvOpen->Flags |= SRVOPEN_FLAG_FOBX_USED;
477         }
478 
479         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
480         {
481             SrvOpen->Context = Add2Ptr(SrvOpen, sizeof(SRV_OPEN));
482         }
483 
484         InitializeListHead(&SrvOpen->SrvOpenQLinks);
485     }
486 
487     /* If we have allocated a FOBX, initialize it */
488     if (Fobx != NULL)
489     {
490         ZeroAndInitializeNodeType(Fobx, RDBSS_NTC_FOBX, FobxSize);
491 
492         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
493         {
494             Fobx->Context = Add2Ptr(Fobx, sizeof(FOBX));
495         }
496     }
497 
498     /* If we have allocated a FCB, initialize it */
499     if (Fcb != NULL)
500     {
501         ZeroAndInitializeNodeType(Fcb, RDBSS_STORAGE_NTC(FileTypeNotYetKnown), FcbSize);
502 
503         Fcb->NonPaged = NonPagedFcb;
504         ZeroAndInitializeNodeType(Fcb->NonPaged, RDBSS_NTC_NONPAGED_FCB, sizeof(NON_PAGED_FCB));
505 #if DBG
506         Fcb->CopyOfNonPaged = NonPagedFcb;
507         NonPagedFcb->FcbBackPointer = Fcb;
508 #endif
509 
510         Fcb->InternalSrvOpen = SrvOpen;
511         Fcb->InternalFobx = Fobx;
512 
513         Fcb->PrivateAlreadyPrefixedName.Length = NameSize;
514         Fcb->PrivateAlreadyPrefixedName.MaximumLength = NameSize;
515         Fcb->PrivateAlreadyPrefixedName.Buffer = PAPNBuffer;
516 
517         if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FCB_EXTENSION))
518         {
519             Fcb->Context = Add2Ptr(Fcb, sizeof(FCB));
520         }
521 
522         ZeroAndInitializeNodeType(&Fcb->FcbTableEntry, RDBSS_NTC_FCB_TABLE_ENTRY, sizeof(RX_FCB_TABLE_ENTRY));
523 
524         InterlockedIncrement(&RxNumberOfActiveFcbs);
525         InterlockedIncrement((volatile long *)&RxDeviceObject->NumberOfActiveFcbs);
526 
527         ExInitializeFastMutex(&NonPagedFcb->AdvancedFcbHeaderMutex);
528         FsRtlSetupAdvancedHeader(Fcb, &NonPagedFcb->AdvancedFcbHeaderMutex);
529     }
530 
531     DPRINT("Allocated %p\n", Buffer);
532 
533     return Buffer;
534 }
535 
536 /*
537  * @implemented
538  */
539 PVOID
540 RxAllocateObject(
541     NODE_TYPE_CODE NodeType,
542     PMINIRDR_DISPATCH MRxDispatch,
543     ULONG NameLength)
544 {
545     ULONG Tag, ObjectSize;
546     PVOID Object, *Extension;
547     PRX_PREFIX_ENTRY PrefixEntry;
548     USHORT StructSize, ExtensionSize;
549 
550     PAGED_CODE();
551 
552     /* Select the node to allocate and always deal with the fact we may have to manage its extension */
553     ExtensionSize = 0;
554     switch (NodeType)
555     {
556         case RDBSS_NTC_SRVCALL:
557             Tag = RX_SRVCALL_POOLTAG;
558             StructSize = sizeof(SRV_CALL);
559             if (MRxDispatch != NULL && BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_SRV_CALL_EXTENSION))
560             {
561                 ExtensionSize = QuadAlign(MRxDispatch->MRxSrvCallSize);
562             }
563             break;
564 
565         case RDBSS_NTC_NETROOT:
566             Tag = RX_NETROOT_POOLTAG;
567             StructSize = sizeof(NET_ROOT);
568             if (BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_NET_ROOT_EXTENSION))
569             {
570                 ExtensionSize = QuadAlign(MRxDispatch->MRxNetRootSize);
571             }
572             break;
573 
574         case RDBSS_NTC_V_NETROOT:
575             Tag = RX_V_NETROOT_POOLTAG;
576             StructSize = sizeof(V_NET_ROOT);
577             if (BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_V_NET_ROOT_EXTENSION))
578             {
579                 ExtensionSize = QuadAlign(MRxDispatch->MRxVNetRootSize);
580             }
581             break;
582 
583         default:
584             ASSERT(FALSE);
585             break;
586     }
587 
588     /* Now, allocate the object */
589     ObjectSize = ExtensionSize + StructSize + NameLength;
590     Object = RxAllocatePoolWithTag(NonPagedPool, ObjectSize, Tag);
591     if (Object == NULL)
592     {
593         return NULL;
594     }
595     /* Initialize it */
596     ZeroAndInitializeNodeType(Object, NodeType, ObjectSize);
597 
598     /* For SRV_CALL and NETROOT, the name points to the prefix table name */
599     switch (NodeType)
600     {
601         case RDBSS_NTC_SRVCALL:
602             PrefixEntry = &((PSRV_CALL)Object)->PrefixEntry;
603             Extension = &((PSRV_CALL)Object)->Context;
604             ((PSRV_CALL)Object)->pSrvCallName = &PrefixEntry->Prefix;
605             break;
606 
607         case RDBSS_NTC_NETROOT:
608             PrefixEntry = &((PNET_ROOT)Object)->PrefixEntry;
609             Extension = &((PNET_ROOT)Object)->Context;
610             ((PNET_ROOT)Object)->pNetRootName = &PrefixEntry->Prefix;
611             break;
612 
613         case RDBSS_NTC_V_NETROOT:
614             PrefixEntry = &((PV_NET_ROOT)Object)->PrefixEntry;
615             Extension = &((PV_NET_ROOT)Object)->Context;
616             break;
617 
618         default:
619             ASSERT(FALSE);
620             break;
621     }
622 
623     /* Set the prefix table unicode string */
624     RtlZeroMemory(PrefixEntry, sizeof(RX_PREFIX_ENTRY));
625     PrefixEntry->NodeTypeCode = RDBSS_NTC_PREFIX_ENTRY;
626     PrefixEntry->NodeByteSize = sizeof(RX_PREFIX_ENTRY);
627     PrefixEntry->Prefix.Length = NameLength;
628     PrefixEntry->Prefix.MaximumLength = NameLength;
629     PrefixEntry->Prefix.Buffer = Add2Ptr(Object, ExtensionSize + StructSize);
630 
631     /* Return the extension if we are asked to manage it */
632     if (ExtensionSize != 0)
633     {
634         *Extension = Add2Ptr(Object, StructSize);
635     }
636 
637     return Object;
638 }
639 
640 /*
641  * @implemented
642  */
643 VOID
644 RxAssert(
645     PVOID Assert,
646     PVOID File,
647     ULONG Line,
648     PVOID Message)
649 {
650     CHAR Response[2];
651     CONTEXT Context;
652 
653     /* If we're not asked to continue, just stop the system */
654     if (!RxContinueFromAssert)
655     {
656         KeBugCheckEx(RDBSS_FILE_SYSTEM, RDBSS_BUG_CHECK_ASSERT | Line, 0, 0, 0);
657     }
658 
659     /* Otherwise, capture context to offer the user to dump it */
660     RtlCaptureContext(&Context);
661 
662     /* Loop until the user hits 'i' */
663     while (TRUE)
664     {
665         /* If no file provided, use empty name */
666         if (File == NULL)
667         {
668             File = "";
669         }
670 
671         /* If no message provided, use empty one */
672         if (Message == NULL)
673         {
674             Message = "";
675         }
676 
677         /* Display the message */
678         DbgPrint("\n*** Assertion failed: %s%s\n***   Source File: %s, line %ld\n\n", Message, Assert, File, Line);
679         /* And ask the user */
680         DbgPrompt("Break, Ignore (bi)? ", Response, sizeof(Response));
681         /* If he asks for ignore, quit
682          * In case of invalid input, ask again
683          */
684         if (Response[0] != 'B' && Response[0] != 'b')
685         {
686             if (Response[0] == 'I' || Response[0] == 'i')
687             {
688                 return;
689             }
690 
691             continue;
692         }
693 
694         /* Break: offer the user to dump the context and break */
695         DbgPrint("Execute '!cxr %lx' to dump context\n", &Context);
696         DbgBreakPoint();
697 
698         /* Continue looping, so that after dump, execution can continue (with ignore) */
699     }
700 }
701 
702 /*
703  * @implemented
704  */
705 VOID
706 NTAPI
707 RxBootstrapWorkerThreadDispatcher(
708    IN PVOID WorkQueue)
709 {
710     PRX_WORK_QUEUE RxWorkQueue;
711 
712     PAGED_CODE();
713 
714     RxWorkQueue = WorkQueue;
715     RxpWorkerThreadDispatcher(RxWorkQueue, NULL);
716 }
717 
718 /*
719  * @implemented
720  */
721 NTSTATUS
722 NTAPI
723 RxChangeBufferingState(
724     PSRV_OPEN SrvOpen,
725     PVOID Context,
726     BOOLEAN ComputeNewState)
727 {
728     PFCB Fcb;
729     NTSTATUS Status, MiniStatus;
730     ULONG NewBufferingState, OldBufferingState;
731 
732     PAGED_CODE();
733 
734     DPRINT("RxChangeBufferingState(%p, %p, %d)\n", SrvOpen, Context, ComputeNewState);
735 
736     Fcb = (PFCB)SrvOpen->pFcb;
737     ASSERT(NodeTypeIsFcb(Fcb));
738     /* First of all, mark that buffering state is changing */
739     SetFlag(Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING);
740 
741     /* Assume success */
742     Status = STATUS_SUCCESS;
743     _SEH2_TRY
744     {
745         /* If we're asked to compute a new state, ask the mini-rdr for it */
746         if (ComputeNewState)
747         {
748             MINIRDR_CALL_THROUGH(MiniStatus, Fcb->MRxDispatch, MRxComputeNewBufferingState,
749                                  ((PMRX_SRV_OPEN)SrvOpen, Context, &NewBufferingState));
750             if (MiniStatus != STATUS_SUCCESS)
751             {
752                 NewBufferingState = 0;
753             }
754         }
755         else
756         {
757             /* If not, use SRV_OPEN state */
758             NewBufferingState = SrvOpen->BufferingFlags;
759         }
760 
761         /* If no shared access, and if we're not asked to compute a new state, use maximum flags set */
762         if ((Fcb->ShareAccess.SharedRead + Fcb->ShareAccess.SharedWrite + Fcb->ShareAccess.SharedDelete) == 0 && !ComputeNewState)
763         {
764             SetFlag(NewBufferingState, FCB_STATE_BUFFERING_STATE_WITH_NO_SHARES);
765         }
766 
767         /* If there's a lock operation to complete, clear that flag */
768         if (Fcb->OutstandingLockOperationsCount != 0)
769         {
770             ClearFlag(NewBufferingState, FCB_STATE_LOCK_BUFFERING_ENABLED);
771         }
772 
773         /* Get the old state */
774         OldBufferingState = Fcb->FcbState & FCB_STATE_BUFFERING_STATE_MASK;
775         DPRINT("ChangeBufferingState %x -> %x (%x)\n", OldBufferingState, NewBufferingState, SrvOpen->BufferingFlags);
776 
777         /* If we're dropping write cache, then flush the FCB */
778         if (BooleanFlagOn(OldBufferingState, FCB_STATE_WRITECACHING_ENABLED) &&
779             !BooleanFlagOn(NewBufferingState, FCB_STATE_WRITECACHING_ENABLED))
780         {
781             DPRINT("Flushing\n");
782 
783             Status = RxFlushFcbInSystemCache(Fcb, TRUE);
784         }
785 
786         /* If we're dropping read cache, then purge */
787         if (Fcb->UncleanCount == 0 ||
788             (BooleanFlagOn(OldBufferingState, FCB_STATE_READCACHING_ENABLED) &&
789              !BooleanFlagOn(NewBufferingState, FCB_STATE_READCACHING_ENABLED)) ||
790             BooleanFlagOn(NewBufferingState, FCB_STATE_DELETE_ON_CLOSE))
791         {
792             DPRINT("Purging\n");
793 
794             if (!NT_SUCCESS(Status))
795             {
796                  DPRINT("Previous flush failed with status: %lx\n", Status);
797             }
798 
799             CcPurgeCacheSection(&Fcb->NonPaged->SectionObjectPointers, NULL, 0, TRUE);
800         }
801 
802         /* If there's already a change pending in SRV_OPEN */
803         if (ComputeNewState && BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING))
804         {
805             /* If there's a FOBX at least */
806             if (!IsListEmpty(&SrvOpen->FobxList))
807             {
808                 PRX_CONTEXT RxContext;
809 
810                 /* Create a fake context to pass to the mini-rdr */
811                 RxContext = RxCreateRxContext(NULL, Fcb->RxDeviceObject, RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING | RX_CONTEXT_FLAG_WAIT);
812                 if (RxContext != NULL)
813                 {
814                     PFOBX Fobx;
815 
816                     RxContext->pFcb = RX_GET_MRX_FCB(Fcb);
817 
818                     /* Give the first FOBX */
819                     Fobx = CONTAINING_RECORD(SrvOpen->FobxList.Flink, FOBX, FobxQLinks);
820                     RxContext->pFobx = (PMRX_FOBX)Fobx;
821                     RxContext->pRelevantSrvOpen = Fobx->pSrvOpen;
822 
823                     /* If there was a delayed close, perform it */
824                     if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_CLOSE_DELAYED))
825                     {
826                         DPRINT("Oplock break close for %p\n", SrvOpen);
827 
828                         RxCloseAssociatedSrvOpen(Fobx, RxContext);
829                     }
830                     /* Otherwise, inform the mini-rdr about completion */
831                     else
832                     {
833                         MINIRDR_CALL_THROUGH(MiniStatus, Fcb->MRxDispatch, MRxCompleteBufferingStateChangeRequest,
834                                              (RxContext, (PMRX_SRV_OPEN)SrvOpen, Context));
835                         (void)MiniStatus;
836                     }
837 
838                     RxDereferenceAndDeleteRxContext(RxContext);
839                 }
840             }
841         }
842 
843         /* Set the new state */
844         Fcb->FcbState ^= (NewBufferingState ^ Fcb->FcbState) & FCB_STATE_BUFFERING_STATE_MASK;
845     }
846     _SEH2_FINALLY
847     {
848         /* Job done, clear the flag */
849         ClearFlag(Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING);
850 
851         if (!BooleanFlagOn(NewBufferingState, FCB_STATE_FILETIMECACHEING_ENABLED))
852         {
853             ClearFlag(Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET);
854         }
855     }
856     _SEH2_END;
857 
858     return Status;
859 }
860 
861 NTSTATUS
862 RxCheckVNetRootCredentials(
863     PRX_CONTEXT RxContext,
864     PV_NET_ROOT VNetRoot,
865     PLUID LogonId,
866     PUNICODE_STRING UserName,
867     PUNICODE_STRING UserDomain,
868     PUNICODE_STRING Password,
869     ULONG Flags)
870 {
871     PAGED_CODE();
872 
873     /* If that's a UNC name, there's nothing to process */
874     if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME) &&
875         (BooleanFlagOn(VNetRoot->Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE) ||
876          Flags != 0))
877     {
878         return STATUS_MORE_PROCESSING_REQUIRED;
879     }
880 
881     /* Compare the logon ID in the VNetRoot with the one provided */
882     if (RtlCompareMemory(&VNetRoot->LogonId, LogonId, sizeof(LUID)) != sizeof(LUID))
883     {
884         return STATUS_MORE_PROCESSING_REQUIRED;
885     }
886 
887     /* No credential provided? That's OK */
888     if (UserName == NULL && UserDomain == NULL && Password == NULL)
889     {
890         return STATUS_SUCCESS;
891     }
892 
893     /* Left to do! */
894     UNIMPLEMENTED;
895     return STATUS_NOT_IMPLEMENTED;
896 }
897 
898 NTSTATUS
899 RxCompleteRequest(
900       PRX_CONTEXT Context,
901       NTSTATUS Status)
902 {
903     PIRP Irp;
904 
905     PAGED_CODE();
906 
907     DPRINT("RxCompleteRequest(%p, %lx)\n", Context, Status);
908 
909     ASSERT(Context != NULL);
910     ASSERT(Context->CurrentIrp != NULL);
911     Irp = Context->CurrentIrp;
912 
913     /* Debug what the caller asks for */
914     if (Context->LoudCompletionString != NULL)
915     {
916         DPRINT("LoudCompletion: %lx/%lx with %wZ\n", Status, Irp->IoStatus.Information, Context->LoudCompletionString);
917         /* Does the user asks to stop on failed completion */
918         if (!NT_SUCCESS(Status) && RxStopOnLoudCompletion)
919         {
920             DPRINT1("LoudFailure: %lx/%lx with %wZ\n", Status, Irp->IoStatus.Information, Context->LoudCompletionString);
921         }
922     }
923 
924     /* Complete for real */
925     Context->CurrentIrp = NULL;
926     RxCompleteRequest_Real(Context, Irp, Status);
927 
928     DPRINT("Status: %lx\n", Status);
929     return Status;
930 }
931 
932 /*
933  * @implemented
934  */
935 VOID
936 RxCompleteRequest_Real(
937     IN PRX_CONTEXT RxContext,
938     IN PIRP Irp,
939     IN NTSTATUS Status)
940 {
941     CCHAR Boost;
942     KIRQL OldIrql;
943     PIO_STACK_LOCATION Stack;
944 
945     DPRINT("RxCompleteRequest_Real(%p, %p, %lx)\n", RxContext, Irp, Status);
946 
947     /* Nothing to complete, just free context */
948     if (Irp == NULL)
949     {
950         DPRINT("NULL IRP for %p\n", RxContext);
951         if (RxContext != NULL)
952         {
953             RxDereferenceAndDeleteRxContext_Real(RxContext);
954         }
955 
956         return;
957     }
958 
959     /* Remove cancel routine */
960     IoAcquireCancelSpinLock(&OldIrql);
961     IoSetCancelRoutine(Irp, NULL);
962     IoReleaseCancelSpinLock(OldIrql);
963 
964     /* Select the boost, given the success/paging operation */
965     if (NT_SUCCESS(Status) || !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO))
966     {
967         Boost = IO_DISK_INCREMENT;
968     }
969     else
970     {
971         Irp->IoStatus.Information = 0;
972         Boost = IO_NO_INCREMENT;
973     }
974     Irp->IoStatus.Status = Status;
975 
976     if (RxContext != NULL)
977     {
978         ASSERT(RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION);
979         if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL)
980         {
981             DPRINT("Completing: MN: %d, Context: %p, IRP: %p, Status: %lx, Info: %lx, #%lx\n",
982                    RxContext->MinorFunction, RxContext, Irp,
983                    Status, Irp->IoStatus.Information, RxContext->SerialNumber);
984         }
985     }
986 
987     /* If that's an opening, there might be a canonical name allocated,
988      * if completion isn't pending, release it
989      */
990     Stack = IoGetCurrentIrpStackLocation(Irp);
991     if (Stack->MajorFunction == IRP_MJ_CREATE && Status != STATUS_PENDING &&
992         RxContext != NULL)
993     {
994         if (BooleanFlagOn(RxContext->Create.Flags, 2))
995         {
996             Stack->FileObject->FileName.Length += sizeof(WCHAR);
997         }
998 
999         RxpPrepareCreateContextForReuse(RxContext);
1000         ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
1001     }
1002 
1003     /* If it's a write, validate the correct behavior of the operation */
1004     if (Stack->MajorFunction == IRP_MJ_WRITE)
1005     {
1006         if (NT_SUCCESS(Irp->IoStatus.Status))
1007         {
1008             ASSERT(Irp->IoStatus.Information <= Stack->Parameters.Write.Length);
1009         }
1010     }
1011 
1012     /* If it's pending, make sure IRP is marked as such */
1013     if (RxContext != NULL)
1014     {
1015         if (RxContext->PendingReturned)
1016         {
1017             ASSERT(BooleanFlagOn(Stack->Control, SL_PENDING_RETURNED));
1018         }
1019     }
1020 
1021     /* Complete now */
1022     DPRINT("Completing IRP with %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
1023     IoCompleteRequest(Irp, Boost);
1024 
1025     /* If there's a context, dereference it */
1026     if (RxContext != NULL)
1027     {
1028         RxDereferenceAndDeleteRxContext_Real(RxContext);
1029     }
1030 }
1031 
1032 /*
1033  * @implemented
1034  */
1035 VOID
1036 RxCompleteSrvOpenKeyAssociation(
1037     IN OUT PSRV_OPEN SrvOpen)
1038 {
1039     PSRV_CALL SrvCall;
1040 
1041     SrvCall = (PSRV_CALL)((PFCB)SrvOpen->pFcb)->VNetRoot->pNetRoot->pSrvCall;
1042     /* Only handle requests if opening was a success */
1043     if (SrvOpen->Condition == Condition_Good)
1044     {
1045         KIRQL OldIrql;
1046         BOOLEAN ProcessChange;
1047         LIST_ENTRY DiscardedRequests;
1048 
1049         /* Initialize our discarded requests list */
1050         InitializeListHead(&DiscardedRequests);
1051 
1052         RxAcquireBufferingManagerMutex(&SrvCall->BufferingManager);
1053 
1054         /* Transfer our requests in the SRV_CALL */
1055         RxTransferList(&SrvCall->BufferingManager.SrvOpenLists[0], &SrvOpen->SrvOpenKeyList);
1056 
1057         /* Was increased in RxInitiateSrvOpenKeyAssociation(), opening is done */
1058         InterlockedDecrement(&SrvCall->BufferingManager.NumberOfOutstandingOpens);
1059 
1060         /* Dispatch requests and get the discarded ones */
1061         RxpDispatchChangeBufferingStateRequests(SrvCall, SrvOpen, &DiscardedRequests);
1062 
1063         RxReleaseBufferingManagerMutex(&SrvCall->BufferingManager);
1064 
1065         /* Is there still anything to process? */
1066         KeAcquireSpinLock(&SrvCall->BufferingManager.SpinLock, &OldIrql);
1067         if (IsListEmpty(&SrvCall->BufferingManager.HandlerList))
1068         {
1069             ProcessChange = FALSE;
1070         }
1071         else
1072         {
1073             ProcessChange = (SrvCall->BufferingManager.HandlerInactive == FALSE);
1074             if (ProcessChange)
1075             {
1076                 SrvCall->BufferingManager.HandlerInactive = TRUE;
1077             }
1078         }
1079         KeReleaseSpinLock(&SrvCall->BufferingManager.SpinLock, OldIrql);
1080 
1081         /* Yes? Go ahead! */
1082         if (ProcessChange)
1083         {
1084             RxReferenceSrvCall(SrvCall);
1085             RxPostToWorkerThread(RxFileSystemDeviceObject, HyperCriticalWorkQueue,
1086                                  &SrvCall->BufferingManager.HandlerWorkItem,
1087                                  RxProcessChangeBufferingStateRequests, SrvCall);
1088         }
1089 
1090         /* And discard left requests */
1091         RxpDiscardChangeBufferingStateRequests(&DiscardedRequests);
1092     }
1093     else
1094     {
1095         InterlockedDecrement(&SrvCall->BufferingManager.NumberOfOutstandingOpens);
1096     }
1097 }
1098 
1099 /*
1100  * @implemented
1101  */
1102 NTSTATUS
1103 RxConstructNetRoot(
1104     IN PRX_CONTEXT RxContext,
1105     IN PSRV_CALL SrvCall,
1106     IN PNET_ROOT NetRoot,
1107     IN PV_NET_ROOT VirtualNetRoot,
1108     OUT PLOCK_HOLDING_STATE LockHoldingState)
1109 {
1110     NTSTATUS Status;
1111     PRX_PREFIX_TABLE PrefixTable;
1112     PMRX_CREATENETROOT_CONTEXT Context;
1113     RX_BLOCK_CONDITION RootCondition, VRootCondition;
1114 
1115     PAGED_CODE();
1116 
1117     DPRINT("RxConstructNetRoot(%p, %p, %p, %p, %p)\n", RxContext, SrvCall, NetRoot,
1118            VirtualNetRoot, LockHoldingState);
1119 
1120     /* Validate the lock is exclusively held */
1121     PrefixTable = RxContext->RxDeviceObject->pRxNetNameTable;
1122     ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
1123 
1124     /* Allocate the context */
1125     Context = RxAllocatePoolWithTag(PagedPool, sizeof(MRX_CREATENETROOT_CONTEXT), RX_SRVCALL_POOLTAG);
1126     if (Context == NULL)
1127     {
1128         return STATUS_INSUFFICIENT_RESOURCES;
1129     }
1130 
1131     /* We can release lock now */
1132     RxReleasePrefixTableLock(PrefixTable);
1133     *LockHoldingState = LHS_LockNotHeld;
1134 
1135     RootCondition = Condition_Bad;
1136     VRootCondition = Condition_Bad;
1137 
1138     /* Initialize the context */
1139     RtlZeroMemory(Context, sizeof(MRX_CREATENETROOT_CONTEXT));
1140     KeInitializeEvent(&Context->FinishEvent, SynchronizationEvent, FALSE);
1141     Context->RxContext = RxContext;
1142     Context->pVNetRoot = VirtualNetRoot;
1143     Context->Callback = RxCreateNetRootCallBack;
1144 
1145     /* And call the mini-rdr */
1146     MINIRDR_CALL_THROUGH(Status, SrvCall->RxDeviceObject->Dispatch, MRxCreateVNetRoot, (Context));
1147     if (Status == STATUS_PENDING)
1148     {
1149         /* Wait for the mini-rdr to be done */
1150         KeWaitForSingleObject(&Context->FinishEvent, Executive, KernelMode, FALSE, NULL);
1151         /* Update the structures condition according to mini-rdr return */
1152         if (NT_SUCCESS(Context->NetRootStatus))
1153         {
1154             if (NT_SUCCESS(Context->VirtualNetRootStatus))
1155             {
1156                 RootCondition = Condition_Good;
1157                 VRootCondition = Condition_Good;
1158                 Status = STATUS_SUCCESS;
1159             }
1160             else
1161             {
1162                 RootCondition = Condition_Good;
1163                 Status = Context->VirtualNetRootStatus;
1164             }
1165         }
1166         else
1167         {
1168             Status = Context->VirtualNetRootStatus;
1169             if (NT_SUCCESS(Status))
1170             {
1171                 Status = Context->NetRootStatus;
1172             }
1173         }
1174     }
1175     else
1176     {
1177         /* It has to return STATUS_PENDING! */
1178         ASSERT(FALSE);
1179     }
1180 
1181     /* Acquire lock again - for caller lock status will remain unchanged */
1182     ASSERT(*LockHoldingState == LHS_LockNotHeld);
1183     RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
1184     *LockHoldingState = LHS_ExclusiveLockHeld;
1185 
1186     /* Do the transition to the condition got from mini-rdr */
1187     RxTransitionNetRoot(NetRoot, RootCondition);
1188     RxTransitionVNetRoot(VirtualNetRoot, VRootCondition);
1189 
1190     /* Context is not longer needed */
1191     RxFreePoolWithTag(Context, RX_SRVCALL_POOLTAG);
1192 
1193     DPRINT("Status: %x\n", Status);
1194 
1195     return Status;
1196 }
1197 
1198 /*
1199  * @implemented
1200  */
1201 NTSTATUS
1202 RxConstructSrvCall(
1203     IN PRX_CONTEXT RxContext,
1204     IN PSRV_CALL SrvCall,
1205     OUT PLOCK_HOLDING_STATE LockHoldingState)
1206 {
1207     NTSTATUS Status;
1208     PRX_PREFIX_TABLE PrefixTable;
1209     PRDBSS_DEVICE_OBJECT RxDeviceObject;
1210     PMRX_SRVCALLDOWN_STRUCTURE Calldown;
1211     PMRX_SRVCALL_CALLBACK_CONTEXT CallbackContext;
1212 
1213     PAGED_CODE();
1214 
1215     DPRINT("RxConstructSrvCall(%p, %p, %p)\n", RxContext, SrvCall, LockHoldingState);
1216 
1217     /* Validate the lock is exclusively held */
1218     RxDeviceObject = RxContext->RxDeviceObject;
1219     PrefixTable = RxDeviceObject->pRxNetNameTable;
1220     ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
1221 
1222     /* Allocate the context for mini-rdr */
1223     Calldown = RxAllocatePoolWithTag(NonPagedPool, sizeof(MRX_SRVCALLDOWN_STRUCTURE), RX_SRVCALL_POOLTAG);
1224     if (Calldown == NULL)
1225     {
1226         SrvCall->Context = NULL;
1227         SrvCall->Condition = Condition_Bad;
1228         RxReleasePrefixTableLock(PrefixTable);
1229         *LockHoldingState = LHS_LockNotHeld;
1230         return STATUS_INSUFFICIENT_RESOURCES;
1231     }
1232 
1233     /* Initialize it */
1234     RtlZeroMemory(Calldown, sizeof(MRX_SRVCALLDOWN_STRUCTURE));
1235 
1236     SrvCall->Context = NULL;
1237     SrvCall->Condition = Condition_InTransition;
1238 
1239     RxReleasePrefixTableLock(PrefixTable);
1240     *LockHoldingState = LHS_LockNotHeld;
1241 
1242     CallbackContext = &Calldown->CallbackContexts[0];
1243     DPRINT("CalldownContext %p for %wZ\n", CallbackContext, &RxDeviceObject->DeviceName);
1244     DPRINT("With calldown %p and SrvCall %p\n", Calldown, SrvCall);
1245     CallbackContext->SrvCalldownStructure = Calldown;
1246     CallbackContext->CallbackContextOrdinal = 0;
1247     CallbackContext->RxDeviceObject = RxDeviceObject;
1248 
1249     RxReferenceSrvCall(SrvCall);
1250 
1251     /* If we're async, we'll post, otherwise, we'll have to wait for completion */
1252     if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
1253     {
1254         RxPrePostIrp(RxContext, RxContext->CurrentIrp);
1255     }
1256     else
1257     {
1258         KeInitializeEvent(&Calldown->FinishEvent, SynchronizationEvent, FALSE);
1259     }
1260 
1261     Calldown->NumberToWait = 1;
1262     Calldown->NumberRemaining = 1;
1263     Calldown->RxContext = RxContext;
1264     Calldown->SrvCall = (PMRX_SRV_CALL)SrvCall;
1265     Calldown->CallBack = RxCreateSrvCallCallBack;
1266     Calldown->BestFinisher = NULL;
1267     CallbackContext->Status = STATUS_BAD_NETWORK_PATH;
1268     InitializeListHead(&Calldown->SrvCalldownList);
1269 
1270     /* Call the mini-rdr */
1271     ASSERT(RxDeviceObject->Dispatch != NULL);
1272     ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
1273     ASSERT(RxDeviceObject->Dispatch->MRxCreateSrvCall != NULL);
1274     Status = RxDeviceObject->Dispatch->MRxCreateSrvCall((PMRX_SRV_CALL)SrvCall, CallbackContext);
1275     /* It has to return STATUS_PENDING! */
1276     ASSERT(Status == STATUS_PENDING);
1277 
1278     /* No async, start completion */
1279     if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
1280     {
1281         KeWaitForSingleObject(&Calldown->FinishEvent, Executive, KernelMode, FALSE, NULL);
1282 
1283         /* Finish construction - we'll notify mini-rdr it's the winner */
1284         Status = RxFinishSrvCallConstruction(Calldown);
1285         if (!NT_SUCCESS(Status))
1286         {
1287             RxReleasePrefixTableLock(PrefixTable);
1288             *LockHoldingState = LHS_LockNotHeld;
1289         }
1290         else
1291         {
1292             ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
1293             *LockHoldingState = LHS_ExclusiveLockHeld;
1294         }
1295     }
1296 
1297     DPRINT("RxConstructSrvCall() = Status: %x\n", Status);
1298     return Status;
1299 }
1300 
1301 /*
1302  * @implemented
1303  */
1304 NTSTATUS
1305 RxConstructVirtualNetRoot(
1306     IN PRX_CONTEXT RxContext,
1307     IN PUNICODE_STRING CanonicalName,
1308     IN NET_ROOT_TYPE NetRootType,
1309     OUT PV_NET_ROOT *VirtualNetRootPointer,
1310     OUT PLOCK_HOLDING_STATE LockHoldingState,
1311     OUT PRX_CONNECTION_ID RxConnectionId)
1312 {
1313     NTSTATUS Status;
1314     PV_NET_ROOT VNetRoot;
1315     RX_BLOCK_CONDITION Condition;
1316     UNICODE_STRING LocalNetRootName, FilePathName;
1317 
1318     PAGED_CODE();
1319 
1320     ASSERT(*LockHoldingState != LHS_LockNotHeld);
1321 
1322     VNetRoot = NULL;
1323     Condition = Condition_Bad;
1324     /* Before creating the VNetRoot, try to find the appropriate connection */
1325     Status = RxFindOrCreateConnections(RxContext, CanonicalName, NetRootType,
1326                                        &LocalNetRootName, &FilePathName,
1327                                        LockHoldingState, RxConnectionId);
1328     /* Found and active */
1329     if (Status == STATUS_CONNECTION_ACTIVE)
1330     {
1331         /* We need a new VNetRoot */
1332         VNetRoot = RxCreateVNetRoot(RxContext, (PNET_ROOT)RxContext->Create.pVNetRoot->pNetRoot,
1333                                     CanonicalName, &LocalNetRootName, &FilePathName, RxConnectionId);
1334         if (VNetRoot != NULL)
1335         {
1336             RxReferenceVNetRoot(VNetRoot);
1337         }
1338 
1339         /* Dereference previous VNetRoot */
1340         RxDereferenceVNetRoot(RxContext->Create.pVNetRoot->pNetRoot, *LockHoldingState);
1341         /* Reset and start construct (new structures will replace old ones) */
1342         RxContext->Create.pSrvCall = NULL;
1343         RxContext->Create.pNetRoot = NULL;
1344         RxContext->Create.pVNetRoot = NULL;
1345 
1346         /* Construct new NetRoot */
1347         if (VNetRoot != NULL)
1348         {
1349             Status = RxConstructNetRoot(RxContext, (PSRV_CALL)VNetRoot->pNetRoot->pSrvCall,
1350                                         (PNET_ROOT)VNetRoot->pNetRoot, VNetRoot, LockHoldingState);
1351             if (NT_SUCCESS(Status))
1352             {
1353                 Condition = Condition_Good;
1354             }
1355         }
1356         else
1357         {
1358             Status = STATUS_INSUFFICIENT_RESOURCES;
1359         }
1360     }
1361     else
1362     {
1363         /* If it failed creating the connection, leave */
1364         if (Status != STATUS_SUCCESS)
1365         {
1366             if (*LockHoldingState != LHS_LockNotHeld)
1367             {
1368                 RxReleasePrefixTableLock(RxContext->RxDeviceObject->pRxNetNameTable);
1369                 *LockHoldingState = LHS_LockNotHeld;
1370             }
1371 
1372             *VirtualNetRootPointer = VNetRoot;
1373             DPRINT("RxConstructVirtualNetRoot() = Status: %x\n", Status);
1374             return Status;
1375         }
1376 
1377         *LockHoldingState = LHS_ExclusiveLockHeld;
1378 
1379         VNetRoot = (PV_NET_ROOT)RxContext->Create.pVNetRoot;
1380         Condition = Condition_Good;
1381     }
1382 
1383     /* We have a non stable VNetRoot - transition it */
1384     if (VNetRoot != NULL && !StableCondition(VNetRoot->Condition))
1385     {
1386         RxTransitionVNetRoot(VNetRoot, Condition);
1387     }
1388 
1389     /* If recreation failed */
1390     if (Status != STATUS_SUCCESS)
1391     {
1392         /* Dereference potential VNetRoot */
1393         if (VNetRoot != NULL)
1394         {
1395             ASSERT(*LockHoldingState != LHS_LockNotHeld);
1396             RxDereferenceVNetRoot(VNetRoot, *LockHoldingState);
1397             VNetRoot = NULL;
1398         }
1399 
1400         /* Release lock */
1401         if (*LockHoldingState != LHS_LockNotHeld)
1402         {
1403             RxReleasePrefixTableLock(RxContext->RxDeviceObject->pRxNetNameTable);
1404             *LockHoldingState = LHS_LockNotHeld;
1405         }
1406 
1407         /* Set NULL ptr */
1408         *VirtualNetRootPointer = VNetRoot;
1409         return Status;
1410     }
1411 
1412     /* Return the allocated VNetRoot */
1413     *VirtualNetRootPointer = VNetRoot;
1414     return Status;
1415 }
1416 
1417 /*
1418  * @implemented
1419  */
1420 PFCB
1421 RxCreateNetFcb(
1422     IN PRX_CONTEXT RxContext,
1423     IN PV_NET_ROOT VNetRoot,
1424     IN PUNICODE_STRING Name)
1425 {
1426     PFCB Fcb;
1427     BOOLEAN FakeFcb;
1428     PNET_ROOT NetRoot;
1429     POOL_TYPE PoolType;
1430     NODE_TYPE_CODE NodeType;
1431     PIO_STACK_LOCATION Stack;
1432     PRDBSS_DEVICE_OBJECT RxDeviceObject;
1433 
1434     PAGED_CODE();
1435 
1436     /* We need a decent VNetRoot */
1437     ASSERT(VNetRoot != NULL && NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT);
1438 
1439     NetRoot = (PNET_ROOT)VNetRoot->pNetRoot;
1440     ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
1441     ASSERT((PMRX_NET_ROOT)NetRoot == RxContext->Create.pNetRoot);
1442 
1443     RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject;
1444     ASSERT(RxDeviceObject == RxContext->RxDeviceObject);
1445 
1446     Stack = RxContext->CurrentIrpSp;
1447 
1448     /* Do we need to create a fake FCB? Like for renaming */
1449     FakeFcb = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY) &&
1450               !BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_SUPPORTS_SYMBOLIC_LINKS);
1451     ASSERT(FakeFcb || RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
1452 
1453     PoolType = (BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE) ? NonPagedPool : PagedPool);
1454     NodeType = (FakeFcb) ? RDBSS_NTC_OPENTARGETDIR_FCB : RDBSS_STORAGE_NTC(FileTypeNotYetKnown);
1455 
1456     /* Allocate the FCB */
1457     Fcb = RxAllocateFcbObject(RxDeviceObject, NodeType, PoolType,
1458                               NetRoot->InnerNamePrefix.Length + Name->Length, NULL);
1459     if (Fcb == NULL)
1460     {
1461         return NULL;
1462     }
1463 
1464     /* Initialize the FCB */
1465     Fcb->CachedNetRootType = NetRoot->Type;
1466     Fcb->RxDeviceObject = RxDeviceObject;
1467     Fcb->MRxDispatch = RxDeviceObject->Dispatch;
1468     Fcb->VNetRoot = VNetRoot;
1469     Fcb->pNetRoot = VNetRoot->pNetRoot;
1470 
1471     InitializeListHead(&Fcb->SrvOpenList);
1472     Fcb->SrvOpenListVersion = 0;
1473 
1474     Fcb->FcbTableEntry.Path.Length = Name->Length;
1475     Fcb->FcbTableEntry.Path.MaximumLength = Name->Length;
1476     Fcb->FcbTableEntry.Path.Buffer = Add2Ptr(Fcb->PrivateAlreadyPrefixedName.Buffer, NetRoot->InnerNamePrefix.Length);
1477     RtlMoveMemory(Fcb->PrivateAlreadyPrefixedName.Buffer, NetRoot->InnerNamePrefix.Buffer,
1478                   NetRoot->InnerNamePrefix.Length);
1479     RtlMoveMemory(Fcb->FcbTableEntry.Path.Buffer, Name->Buffer, Name->Length);
1480 
1481     /* Copy back parameters from RxContext */
1482     if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_ADDEDBACKSLASH))
1483     {
1484         Fcb->FcbState |= FCB_STATE_ADDEDBACKSLASH;
1485     }
1486 
1487     InitializeListHead(&Fcb->NonPaged->TransitionWaitList);
1488 
1489     if (BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE))
1490     {
1491         Fcb->FcbState |= FCB_STATE_PAGING_FILE;
1492     }
1493 
1494     if (RxContext->MajorFunction == IRP_MJ_CREATE && BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_SPECIAL_PATH))
1495     {
1496         Fcb->FcbState |= FCB_STATE_SPECIAL_PATH;
1497     }
1498 
1499     Fcb->Header.Resource = &Fcb->NonPaged->HeaderResource;
1500     ExInitializeResourceLite(Fcb->Header.Resource);
1501 
1502     Fcb->Header.PagingIoResource = &Fcb->NonPaged->PagingIoResource;
1503     ExInitializeResourceLite(Fcb->Header.PagingIoResource);
1504 
1505     Fcb->BufferedLocks.Resource = &Fcb->NonPaged->BufferedLocksResource;
1506     ExInitializeResourceLite(Fcb->BufferedLocks.Resource);
1507 
1508     /* Fake FCB doesn't go in prefix table */
1509     if (FakeFcb)
1510     {
1511         Fcb->FcbState |= (FCB_STATE_FAKEFCB | FCB_STATE_NAME_ALREADY_REMOVED);
1512         InitializeListHead(&Fcb->FcbTableEntry.HashLinks);
1513         DPRINT("Fake FCB: %p\n", Fcb);
1514     }
1515     else
1516     {
1517         RxFcbTableInsertFcb(&NetRoot->FcbTable, Fcb);
1518     }
1519 
1520     RxReferenceVNetRoot(VNetRoot);
1521     InterlockedIncrement((volatile long *)&Fcb->pNetRoot->NumberOfFcbs);
1522 
1523     Fcb->ulFileSizeVersion = 0;
1524 
1525     DPRINT("FCB %p for %wZ\n", Fcb, &Fcb->FcbTableEntry.Path);
1526     RxReferenceNetFcb(Fcb);
1527 
1528     return Fcb;
1529 }
1530 
1531 /*
1532  * @implemented
1533  */
1534 PMRX_FOBX
1535 NTAPI
1536 RxCreateNetFobx(
1537     OUT PRX_CONTEXT RxContext,
1538     IN PMRX_SRV_OPEN MrxSrvOpen)
1539 {
1540     PFCB Fcb;
1541     PFOBX Fobx;
1542     ULONG Flags;
1543     PNET_ROOT NetRoot;
1544     PSRV_OPEN SrvOpen;
1545     POOL_TYPE PoolType;
1546 
1547     PAGED_CODE();
1548 
1549     SrvOpen = (PSRV_OPEN)MrxSrvOpen;
1550     ASSERT(NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN);
1551     ASSERT(NodeTypeIsFcb(SrvOpen->Fcb));
1552     ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
1553 
1554     Fcb = SrvOpen->Fcb;
1555     PoolType = (BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool);
1556     /* Can we use pre-allocated FOBX? */
1557     if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_FOBX_USED) && Fcb->InternalSrvOpen == (PSRV_OPEN)MrxSrvOpen)
1558     {
1559         Fobx = Fcb->InternalFobx;
1560         /* Call allocate to initialize the FOBX */
1561         RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, Fobx);
1562         /* Mark it used now */
1563         Fcb->FcbState |= FCB_STATE_FOBX_USED;
1564         Flags = FOBX_FLAG_ENCLOSED_ALLOCATED;
1565     }
1566     else if (!BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FOBX_USED))
1567     {
1568         Fobx = SrvOpen->InternalFobx;
1569         /* Call allocate to initialize the FOBX */
1570         RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, Fobx);
1571         /* Mark it used now */
1572         SrvOpen->Flags |= SRVOPEN_FLAG_FOBX_USED;
1573         Flags = FOBX_FLAG_ENCLOSED_ALLOCATED;
1574     }
1575     else
1576     {
1577         /* Last case, we cannot, allocate a FOBX */
1578         Fobx = RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, NULL);
1579         Flags = 0;
1580     }
1581 
1582     /* Allocation failed! */
1583     if (Fobx == NULL)
1584     {
1585         return NULL;
1586     }
1587 
1588     /* Set flags */
1589     Fobx->Flags = Flags;
1590 
1591     /* Initialize throttling */
1592     NetRoot = (PNET_ROOT)RxContext->Create.pNetRoot;
1593     if (NetRoot != NULL)
1594     {
1595         if (NetRoot->DeviceType == FILE_DEVICE_DISK)
1596         {
1597             RxInitializeThrottlingState(&Fobx->Specific.DiskFile.LockThrottlingState,
1598                                         NetRoot->DiskParameters.LockThrottlingParameters.Increment,
1599                                         NetRoot->DiskParameters.LockThrottlingParameters.MaximumDelay);
1600         }
1601         else if (NetRoot->DeviceType == FILE_DEVICE_NAMED_PIPE)
1602         {
1603             RxInitializeThrottlingState(&Fobx->Specific.NamedPipe.ThrottlingState,
1604                                         NetRoot->NamedPipeParameters.PipeReadThrottlingParameters.Increment,
1605                                         NetRoot->NamedPipeParameters.PipeReadThrottlingParameters.MaximumDelay);
1606         }
1607     }
1608 
1609     /* Propagate flags fron RxContext */
1610     if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME))
1611     {
1612         Fobx->Flags |= FOBX_FLAG_UNC_NAME;
1613     }
1614 
1615     if (BooleanFlagOn(RxContext->Create.NtCreateParameters.CreateOptions, FILE_OPEN_FOR_BACKUP_INTENT))
1616     {
1617         Fobx->Flags |= FOBX_FLAG_BACKUP_INTENT;
1618     }
1619 
1620     /* Continue init */
1621     Fobx->FobxSerialNumber = 0;
1622     Fobx->SrvOpen = (PSRV_OPEN)MrxSrvOpen;
1623     Fobx->NodeReferenceCount = 1;
1624     Fobx->RxDeviceObject = Fcb->RxDeviceObject;
1625 
1626     RxReferenceSrvOpen(SrvOpen);
1627     InterlockedIncrement((volatile long *)&SrvOpen->pVNetRoot->NumberOfFobxs);
1628 
1629     InsertTailList(&SrvOpen->FobxList, &Fobx->FobxQLinks);
1630     InitializeListHead(&Fobx->ScavengerFinalizationList);
1631     InitializeListHead(&Fobx->ClosePendingList);
1632 
1633     Fobx->CloseTime.QuadPart = 0;
1634     Fobx->fOpenCountDecremented = FALSE;
1635 
1636     DPRINT("FOBX %p for SRV_OPEN %p FCB %p\n", Fobx, Fobx->SrvOpen, Fobx->SrvOpen->pFcb);
1637 
1638     return (PMRX_FOBX)Fobx;
1639 }
1640 
1641 /*
1642  * @implemented
1643  */
1644 PNET_ROOT
1645 RxCreateNetRoot(
1646     IN PSRV_CALL SrvCall,
1647     IN PUNICODE_STRING Name,
1648     IN ULONG NetRootFlags,
1649     IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
1650 {
1651     PNET_ROOT NetRoot;
1652     USHORT CaseInsensitiveLength;
1653     PRX_PREFIX_TABLE PrefixTable;
1654 
1655     DPRINT("RxCreateNetRoot(%p, %wZ, %x, %p)\n", SrvCall, Name, NetRootFlags, RxConnectionId);
1656 
1657     PAGED_CODE();
1658 
1659     /* We need a SRV_CALL */
1660     ASSERT(SrvCall != NULL);
1661 
1662     PrefixTable = SrvCall->RxDeviceObject->pRxNetNameTable;
1663     ASSERT(RxIsPrefixTableLockExclusive(PrefixTable));
1664 
1665     /* Get name length */
1666     CaseInsensitiveLength = SrvCall->PrefixEntry.Prefix.Length + Name->Length;
1667     if (CaseInsensitiveLength > MAXUSHORT)
1668     {
1669         return NULL;
1670     }
1671 
1672     /* Allocate the NetRoot */
1673     NetRoot = RxAllocateObject(RDBSS_NTC_NETROOT, SrvCall->RxDeviceObject->Dispatch,
1674                                CaseInsensitiveLength);
1675     if (NetRoot == NULL)
1676     {
1677         return NULL;
1678     }
1679 
1680     /* Construct name */
1681     RtlMoveMemory(Add2Ptr(NetRoot->PrefixEntry.Prefix.Buffer, SrvCall->PrefixEntry.Prefix.Length),
1682                   Name->Buffer, Name->Length);
1683     if (SrvCall->PrefixEntry.Prefix.Length != 0)
1684     {
1685         RtlMoveMemory(NetRoot->PrefixEntry.Prefix.Buffer, SrvCall->PrefixEntry.Prefix.Buffer,
1686                       SrvCall->PrefixEntry.Prefix.Length);
1687     }
1688 
1689     if (!BooleanFlagOn(SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS))
1690     {
1691         CaseInsensitiveLength = SrvCall->PrefixEntry.CaseInsensitiveLength;
1692     }
1693     /* Inisert in prefix table */
1694     RxPrefixTableInsertName(PrefixTable, &NetRoot->PrefixEntry, NetRoot,
1695                             (PULONG)&NetRoot->NodeReferenceCount, CaseInsensitiveLength,
1696                             RxConnectionId);
1697 
1698     /* Prepare the FCB table */
1699     RxInitializeFcbTable(&NetRoot->FcbTable, TRUE);
1700 
1701     InitializeListHead(&NetRoot->TransitionWaitList);
1702     InitializeListHead(&NetRoot->ScavengerFinalizationList);
1703     InitializeListHead(&NetRoot->VirtualNetRoots);
1704 
1705     RxInitializePurgeSyncronizationContext(&NetRoot->PurgeSyncronizationContext);
1706 
1707     NetRoot->SerialNumberForEnum = SerialNumber++;
1708     NetRoot->Flags |= NetRootFlags;
1709     NetRoot->DiskParameters.ClusterSize = 1;
1710     NetRoot->DiskParameters.ReadAheadGranularity = ReadAheadGranularity;
1711     NetRoot->SrvCall = SrvCall;
1712 
1713     RxReferenceSrvCall(SrvCall);
1714 
1715     DPRINT("NetRootName: %wZ (%p)\n", NetRoot->pNetRootName, NetRoot);
1716     return NetRoot;
1717 }
1718 
1719 /*
1720  * @implemented
1721  */
1722 VOID
1723 NTAPI
1724 RxCreateNetRootCallBack(
1725     IN PMRX_CREATENETROOT_CONTEXT CreateNetRootContext)
1726 {
1727     PAGED_CODE();
1728 
1729     KeSetEvent(&CreateNetRootContext->FinishEvent, IO_NETWORK_INCREMENT, FALSE);
1730 }
1731 
1732 /*
1733  * @implemented
1734  */
1735 PRX_CONTEXT
1736 NTAPI
1737 RxCreateRxContext(
1738     IN PIRP Irp,
1739     IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
1740     IN ULONG InitialContextFlags)
1741 {
1742     KIRQL OldIrql;
1743     PRX_CONTEXT Context;
1744 
1745     ASSERT(RxDeviceObject != NULL);
1746 
1747     DPRINT("RxCreateRxContext(%p, %p, %u)\n", Irp, RxDeviceObject, InitialContextFlags);
1748 
1749 #if DBG
1750     InterlockedIncrement((volatile LONG *)&RxFsdEntryCount);
1751 #endif
1752     InterlockedIncrement((volatile LONG *)&RxDeviceObject->NumberOfActiveContexts);
1753 
1754     /* Allocate the context from our lookaside list */
1755     Context = ExAllocateFromNPagedLookasideList(&RxContextLookasideList);
1756     if (Context == NULL)
1757     {
1758         return NULL;
1759     }
1760 
1761     /* Zero it */
1762     RtlZeroMemory(Context, sizeof(RX_CONTEXT));
1763 
1764     /* It was allocated on NP pool, keep track of it! */
1765     SetFlag(Context->Flags, RX_CONTEXT_FLAG_FROM_POOL);
1766     /* And initialize it */
1767     RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, Context);
1768     ASSERT((Context->MajorFunction != IRP_MJ_CREATE) || !BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED));
1769 
1770     /* Add it to our global list */
1771     KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
1772     InsertTailList(&RxActiveContexts, &Context->ContextListEntry);
1773     KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
1774 
1775     DPRINT("Context: %p\n", Context);
1776     return Context;
1777 }
1778 
1779 /*
1780  * @implemented
1781  */
1782 PSRV_CALL
1783 RxCreateSrvCall(
1784     IN PRX_CONTEXT RxContext,
1785     IN PUNICODE_STRING Name,
1786     IN PUNICODE_STRING InnerNamePrefix OPTIONAL,
1787     IN PRX_CONNECTION_ID RxConnectionId)
1788 {
1789     ULONG NameLength;
1790     PSRV_CALL SrvCall;
1791 
1792     PAGED_CODE();
1793 
1794     DPRINT("RxCreateSrvCall(%p, %wZ, %wZ, %p)\n", RxContext, Name, InnerNamePrefix, RxConnectionId);
1795 
1796     ASSERT(RxIsPrefixTableLockExclusive(RxContext->RxDeviceObject->pRxNetNameTable));
1797 
1798     /* Get the name length */
1799     NameLength = Name->Length + 2 * sizeof(WCHAR);
1800     if (InnerNamePrefix != NULL)
1801     {
1802         NameLength += InnerNamePrefix->Length;
1803     }
1804 
1805     /* Allocate the object */
1806     SrvCall = RxAllocateObject(RDBSS_NTC_SRVCALL, NULL, NameLength);
1807     if (SrvCall == NULL)
1808     {
1809         return NULL;
1810     }
1811 
1812     /* Initialize it */
1813     SrvCall->SerialNumberForEnum = SerialNumber++;
1814     SrvCall->RxDeviceObject = RxContext->RxDeviceObject;
1815     RxInitializeBufferingManager(SrvCall);
1816     InitializeListHead(&SrvCall->TransitionWaitList);
1817     InitializeListHead(&SrvCall->ScavengerFinalizationList);
1818     RxInitializePurgeSyncronizationContext(&SrvCall->PurgeSyncronizationContext);
1819     RxInitializeSrvCallParameters(RxContext, SrvCall);
1820     RtlMoveMemory(SrvCall->PrefixEntry.Prefix.Buffer, Name->Buffer, Name->Length);
1821     SrvCall->PrefixEntry.Prefix.MaximumLength = Name->Length + 2 * sizeof(WCHAR);
1822     SrvCall->PrefixEntry.Prefix.Length = Name->Length;
1823     RxPrefixTableInsertName(RxContext->RxDeviceObject->pRxNetNameTable, &SrvCall->PrefixEntry,
1824                             SrvCall, (PULONG)&SrvCall->NodeReferenceCount, Name->Length, RxConnectionId);
1825 
1826     DPRINT("SrvCallName: %wZ (%p)\n", SrvCall->pSrvCallName, SrvCall);
1827     return SrvCall;
1828 }
1829 
1830 /*
1831  * @implemented
1832  */
1833 VOID
1834 NTAPI
1835 RxCreateSrvCallCallBack(
1836     IN OUT PMRX_SRVCALL_CALLBACK_CONTEXT Context)
1837 {
1838     KIRQL OldIrql;
1839     PSRV_CALL SrvCall;
1840     PRX_CONTEXT RxContext;
1841     ULONG NumberRemaining;
1842     BOOLEAN StartDispatcher;
1843     PMRX_SRVCALLDOWN_STRUCTURE Calldown;
1844 
1845     DPRINT("RxCreateSrvCallCallBack(%p)\n", Context);
1846 
1847     /* Get our context structures */
1848     Calldown = Context->SrvCalldownStructure;
1849     SrvCall = (PSRV_CALL)Calldown->SrvCall;
1850 
1851     /* If it is a success, that's the winner */
1852     KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
1853     if (Context->Status == STATUS_SUCCESS)
1854     {
1855         Calldown->BestFinisherOrdinal = Context->CallbackContextOrdinal;
1856         Calldown->BestFinisher = Context->RxDeviceObject;
1857     }
1858     NumberRemaining = --Calldown->NumberRemaining;
1859     SrvCall->Status = Context->Status;
1860     KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
1861 
1862     /* Still some to ask, keep going */
1863     if (NumberRemaining != 0)
1864     {
1865         return;
1866     }
1867 
1868     /* If that's not async, signal we're done */
1869     RxContext = Calldown->RxContext;
1870     if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
1871     {
1872         KeSetEvent(&Calldown->FinishEvent, IO_NETWORK_INCREMENT, FALSE);
1873         return;
1874     }
1875     /* If that's a mailslot, finish construction, no more to do */
1876     else if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CREATE_MAILSLOT))
1877     {
1878         RxFinishSrvCallConstruction(Calldown);
1879         return;
1880     }
1881 
1882     /* Queue our finish call for delayed completion */
1883     DPRINT("Queuing RxFinishSrvCallConstruction() call\n");
1884     KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
1885     InsertTailList(&RxSrvCalldownList, &Calldown->SrvCalldownList);
1886     StartDispatcher = !RxSrvCallConstructionDispatcherActive;
1887     KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
1888 
1889     /* If we have to start dispatcher, go ahead */
1890     if (StartDispatcher)
1891     {
1892         NTSTATUS Status;
1893 
1894         Status = RxDispatchToWorkerThread(RxFileSystemDeviceObject, CriticalWorkQueue,
1895                                           RxFinishSrvCallConstructionDispatcher, &RxSrvCalldownList);
1896         if (!NT_SUCCESS(Status))
1897         {
1898             /* It failed - run it manually.... */
1899             RxFinishSrvCallConstructionDispatcher(NULL);
1900         }
1901     }
1902 }
1903 
1904 /*
1905  * @implemented
1906  */
1907 PSRV_OPEN
1908 RxCreateSrvOpen(
1909     IN PV_NET_ROOT VNetRoot,
1910     IN OUT PFCB Fcb)
1911 {
1912     ULONG Flags;
1913     PSRV_OPEN SrvOpen;
1914     POOL_TYPE PoolType;
1915 
1916     PAGED_CODE();
1917 
1918     ASSERT(NodeTypeIsFcb(Fcb));
1919     ASSERT(RxIsFcbAcquiredExclusive(Fcb));
1920 
1921     PoolType = (BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool);
1922 
1923     _SEH2_TRY
1924     {
1925         SrvOpen = Fcb->InternalSrvOpen;
1926         /* Check whethet we have to allocate a new SRV_OPEN */
1927         if (Fcb->InternalSrvOpen == NULL || BooleanFlagOn(Fcb->FcbState, FCB_STATE_SRVOPEN_USED) ||
1928             BooleanFlagOn(Fcb->InternalSrvOpen->Flags, SRVOPEN_FLAG_ENCLOSED_ALLOCATED) ||
1929             !IsListEmpty(&Fcb->InternalSrvOpen->SrvOpenQLinks))
1930         {
1931             /* Proceed */
1932             SrvOpen = RxAllocateFcbObject(Fcb->VNetRoot->NetRoot->pSrvCall->RxDeviceObject,
1933                                           RDBSS_NTC_SRVOPEN, PoolType, 0, NULL);
1934             Flags = 0;
1935         }
1936         else
1937         {
1938             /* Otherwise, just use internal one and initialize it */
1939             RxAllocateFcbObject(Fcb->VNetRoot->NetRoot->pSrvCall->RxDeviceObject,
1940                                 RDBSS_NTC_INTERNAL_SRVOPEN, PoolType, 0,
1941                                 Fcb->InternalSrvOpen);
1942             Fcb->FcbState |= FCB_STATE_SRVOPEN_USED;
1943             Flags = SRVOPEN_FLAG_ENCLOSED_ALLOCATED | SRVOPEN_FLAG_FOBX_USED;
1944         }
1945 
1946         /* If SrvOpen was properly allocated, initialize it */
1947         if (SrvOpen != NULL)
1948         {
1949             SrvOpen->Flags = Flags;
1950             SrvOpen->pFcb = RX_GET_MRX_FCB(Fcb);
1951             SrvOpen->pAlreadyPrefixedName = &Fcb->PrivateAlreadyPrefixedName;
1952             SrvOpen->pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
1953             SrvOpen->ulFileSizeVersion = Fcb->ulFileSizeVersion;
1954             SrvOpen->NodeReferenceCount = 1;
1955 
1956             RxReferenceVNetRoot(VNetRoot);
1957             RxReferenceNetFcb(Fcb);
1958 
1959             InsertTailList(&Fcb->SrvOpenList, &SrvOpen->SrvOpenQLinks);
1960             ++Fcb->SrvOpenListVersion;
1961 
1962             InitializeListHead(&SrvOpen->ScavengerFinalizationList);
1963             InitializeListHead(&SrvOpen->TransitionWaitList);
1964             InitializeListHead(&SrvOpen->FobxList);
1965             InitializeListHead(&SrvOpen->SrvOpenKeyList);
1966         }
1967     }
1968     _SEH2_FINALLY
1969     {
1970         if (_SEH2_AbnormalTermination())
1971         {
1972             if (SrvOpen != NULL)
1973             {
1974                 RxFinalizeSrvOpen(SrvOpen, TRUE, TRUE);
1975                 SrvOpen = NULL;
1976             }
1977         }
1978         else
1979         {
1980             DPRINT("SrvOpen %p for FCB %p\n", SrvOpen, SrvOpen->pFcb);
1981         }
1982     }
1983     _SEH2_END;
1984 
1985     return SrvOpen;
1986 }
1987 
1988 /*
1989  * @implemented
1990  */
1991 PV_NET_ROOT
1992 RxCreateVNetRoot(
1993     IN PRX_CONTEXT RxContext,
1994     IN PNET_ROOT NetRoot,
1995     IN PUNICODE_STRING CanonicalName,
1996     IN PUNICODE_STRING LocalNetRootName,
1997     IN PUNICODE_STRING FilePath,
1998     IN PRX_CONNECTION_ID RxConnectionId)
1999 {
2000     NTSTATUS Status;
2001     PV_NET_ROOT VNetRoot;
2002     USHORT CaseInsensitiveLength;
2003 
2004     PAGED_CODE();
2005 
2006     DPRINT("RxCreateVNetRoot(%p, %p, %wZ, %wZ, %wZ, %p)\n", RxContext, NetRoot, CanonicalName,
2007            LocalNetRootName, FilePath, RxConnectionId);
2008 
2009     /* Lock must be held exclusively */
2010     ASSERT(RxIsPrefixTableLockExclusive(RxContext->RxDeviceObject->pRxNetNameTable));
2011 
2012     /* Check for overflow */
2013     if (LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length > MAXUSHORT)
2014     {
2015         return NULL;
2016     }
2017 
2018     /* Get name length and allocate VNetRoot */
2019     CaseInsensitiveLength = LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length;
2020     VNetRoot = RxAllocateObject(RDBSS_NTC_V_NETROOT, NetRoot->SrvCall->RxDeviceObject->Dispatch,
2021                                 CaseInsensitiveLength);
2022     if (VNetRoot == NULL)
2023     {
2024         return NULL;
2025     }
2026 
2027     /* Initialize its connection parameters */
2028     Status = RxInitializeVNetRootParameters(RxContext, &VNetRoot->LogonId, &VNetRoot->SessionId,
2029                                             &VNetRoot->pUserName, &VNetRoot->pUserDomainName,
2030                                             &VNetRoot->pPassword, &VNetRoot->Flags);
2031     if (!NT_SUCCESS(Status))
2032     {
2033         RxUninitializeVNetRootParameters(VNetRoot->pUserName, VNetRoot->pUserDomainName,
2034                                          VNetRoot->pPassword, &VNetRoot->Flags);
2035         RxFreeObject(VNetRoot);
2036 
2037         return NULL;
2038     }
2039 
2040     /* Set name */
2041     RtlMoveMemory(VNetRoot->PrefixEntry.Prefix.Buffer, CanonicalName->Buffer, VNetRoot->PrefixEntry.Prefix.Length);
2042 
2043     VNetRoot->PrefixOffsetInBytes = LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length;
2044     VNetRoot->NamePrefix.Buffer = Add2Ptr(VNetRoot->PrefixEntry.Prefix.Buffer, VNetRoot->PrefixOffsetInBytes);
2045     VNetRoot->NamePrefix.Length = VNetRoot->PrefixEntry.Prefix.Length - VNetRoot->PrefixOffsetInBytes;
2046     VNetRoot->NamePrefix.MaximumLength = VNetRoot->PrefixEntry.Prefix.Length - VNetRoot->PrefixOffsetInBytes;
2047 
2048     InitializeListHead(&VNetRoot->TransitionWaitList);
2049     InitializeListHead(&VNetRoot->ScavengerFinalizationList);
2050 
2051     if (!BooleanFlagOn(NetRoot->SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_FILENAMES))
2052     {
2053         USHORT i;
2054 
2055         if (BooleanFlagOn(NetRoot->SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS))
2056         {
2057             CaseInsensitiveLength = NetRoot->PrefixEntry.CaseInsensitiveLength;
2058         }
2059         else
2060         {
2061             CaseInsensitiveLength = NetRoot->SrvCall->PrefixEntry.CaseInsensitiveLength;
2062         }
2063 
2064         for (i = 1; i < CanonicalName->Length / sizeof(WCHAR); ++i)
2065         {
2066             if (CanonicalName->Buffer[i] != OBJ_NAME_PATH_SEPARATOR)
2067             {
2068                 break;
2069             }
2070         }
2071 
2072         CaseInsensitiveLength += (i * sizeof(WCHAR));
2073     }
2074 
2075     /* Insert in prefix table */
2076     RxPrefixTableInsertName(RxContext->RxDeviceObject->pRxNetNameTable, &VNetRoot->PrefixEntry,
2077                             VNetRoot, (PULONG)&VNetRoot->NodeReferenceCount, CaseInsensitiveLength,
2078                             RxConnectionId);
2079 
2080     RxReferenceNetRoot(NetRoot);
2081     RxAddVirtualNetRootToNetRoot(NetRoot, VNetRoot);
2082 
2083     /* Finish init */
2084     VNetRoot->SerialNumberForEnum = SerialNumber++;
2085     VNetRoot->UpperFinalizationDone = FALSE;
2086     VNetRoot->ConnectionFinalizationDone = FALSE;
2087     VNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0;
2088 
2089     DPRINT("NamePrefix: %wZ\n", &VNetRoot->NamePrefix);
2090     DPRINT("PrefixEntry: %wZ\n", &VNetRoot->PrefixEntry.Prefix);
2091 
2092     return VNetRoot;
2093 }
2094 
2095 /*
2096  * @implemented
2097  */
2098 VOID
2099 RxDereference(
2100     IN OUT PVOID Instance,
2101     IN LOCK_HOLDING_STATE LockHoldingState)
2102 {
2103     LONG RefCount;
2104     NODE_TYPE_CODE NodeType;
2105     PNODE_TYPE_AND_SIZE Node;
2106 
2107     PAGED_CODE();
2108 
2109     RxAcquireScavengerMutex();
2110 
2111     /* Check we have a node we can handle */
2112     NodeType = NodeType(Instance);
2113     ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
2114            (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
2115            (NodeType == RDBSS_NTC_FOBX));
2116 
2117     Node = (PNODE_TYPE_AND_SIZE)Instance;
2118     RefCount = InterlockedDecrement((volatile long *)&Node->NodeReferenceCount);
2119     ASSERT(RefCount >= 0);
2120 
2121     /* Trace refcount */
2122     switch (NodeType)
2123     {
2124         case RDBSS_NTC_SRVCALL:
2125             PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
2126             break;
2127 
2128         case RDBSS_NTC_NETROOT:
2129             PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
2130             break;
2131 
2132         case RDBSS_NTC_V_NETROOT:
2133             PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
2134             break;
2135 
2136         case RDBSS_NTC_SRVOPEN:
2137             PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
2138             break;
2139 
2140         case RDBSS_NTC_FOBX:
2141             PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
2142             break;
2143 
2144         default:
2145             ASSERT(FALSE);
2146             break;
2147     }
2148 
2149     /* No need to free - still in use */
2150     if (RefCount > 1)
2151     {
2152         RxReleaseScavengerMutex();
2153         return;
2154     }
2155 
2156     /* We have to be locked exclusively */
2157     if (LockHoldingState != LHS_ExclusiveLockHeld)
2158     {
2159         if ((NodeType == RDBSS_NTC_FOBX && RefCount == 0) ||
2160              (NodeType >= RDBSS_NTC_SRVCALL && NodeType <= RDBSS_NTC_V_NETROOT))
2161         {
2162             RxpMarkInstanceForScavengedFinalization(Instance);
2163         }
2164 
2165         RxReleaseScavengerMutex();
2166         return;
2167     }
2168     else
2169     {
2170         if (BooleanFlagOn(NodeType, RX_SCAVENGER_MASK))
2171         {
2172             RxpUndoScavengerFinalizationMarking(Instance);
2173         }
2174     }
2175 
2176     RxReleaseScavengerMutex();
2177 
2178     /* Now, deallocate the memory */
2179     switch (NodeType)
2180     {
2181         case RDBSS_NTC_SRVCALL:
2182         {
2183             PSRV_CALL SrvCall;
2184 
2185             SrvCall = (PSRV_CALL)Instance;
2186 
2187             ASSERT(SrvCall->RxDeviceObject != NULL);
2188             ASSERT(RxIsPrefixTableLockAcquired(SrvCall->RxDeviceObject->pRxNetNameTable));
2189             RxFinalizeSrvCall(SrvCall, TRUE, TRUE);
2190             break;
2191         }
2192 
2193         case RDBSS_NTC_NETROOT:
2194         {
2195             PNET_ROOT NetRoot;
2196 
2197             NetRoot = (PNET_ROOT)Instance;
2198 
2199             ASSERT(NetRoot->pSrvCall->RxDeviceObject != NULL);
2200             ASSERT(RxIsPrefixTableLockAcquired(NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable));
2201             RxFinalizeNetRoot(NetRoot, TRUE, TRUE);
2202             break;
2203         }
2204 
2205         case RDBSS_NTC_V_NETROOT:
2206         {
2207             PV_NET_ROOT VNetRoot;
2208 
2209             VNetRoot = (PV_NET_ROOT)Instance;
2210 
2211             ASSERT(VNetRoot->pNetRoot->pSrvCall->RxDeviceObject != NULL);
2212             ASSERT(RxIsPrefixTableLockAcquired(VNetRoot->pNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable));
2213             RxFinalizeVNetRoot(VNetRoot, TRUE, TRUE);
2214             break;
2215         }
2216 
2217         case RDBSS_NTC_SRVOPEN:
2218         {
2219             PSRV_OPEN SrvOpen;
2220 
2221             SrvOpen = (PSRV_OPEN)Instance;
2222 
2223             ASSERT(RxIsFcbAcquired(SrvOpen->Fcb));
2224             if (SrvOpen->OpenCount == 0)
2225             {
2226                 RxFinalizeSrvOpen(SrvOpen, FALSE, FALSE);
2227             }
2228             break;
2229         }
2230 
2231         case RDBSS_NTC_FOBX:
2232         {
2233             PFOBX Fobx;
2234 
2235             Fobx = (PFOBX)Instance;
2236 
2237             ASSERT(RxIsFcbAcquired(Fobx->SrvOpen->Fcb));
2238             RxFinalizeNetFobx(Fobx, TRUE, FALSE);
2239             break;
2240         }
2241     }
2242 }
2243 
2244 /*
2245  * @implemented
2246  */
2247 VOID
2248 NTAPI
2249 RxDereferenceAndDeleteRxContext_Real(
2250     IN PRX_CONTEXT RxContext)
2251 {
2252     KIRQL OldIrql;
2253     ULONG RefCount;
2254     BOOLEAN Allocated;
2255     PRX_CONTEXT StopContext = NULL;
2256 
2257     /* Make sure we really have a context */
2258     KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
2259     ASSERT(RxContext->NodeTypeCode == RDBSS_NTC_RX_CONTEXT);
2260     RefCount = InterlockedDecrement((volatile LONG *)&RxContext->ReferenceCount);
2261     /* If refcount is 0, start releasing stuff that needs spinlock held */
2262     if (RefCount == 0)
2263     {
2264         PRDBSS_DEVICE_OBJECT RxDeviceObject;
2265 
2266         Allocated = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_FROM_POOL);
2267 
2268         /* If that's stop context from DO, remove it */
2269         RxDeviceObject = RxContext->RxDeviceObject;
2270         if (RxDeviceObject->StartStopContext.pStopContext == RxContext)
2271         {
2272             RxDeviceObject->StartStopContext.pStopContext = NULL;
2273         }
2274         else
2275         {
2276            /* Remove it from the list */
2277             ASSERT((RxContext->ContextListEntry.Flink->Blink == &RxContext->ContextListEntry) &&
2278                    (RxContext->ContextListEntry.Blink->Flink == &RxContext->ContextListEntry));
2279             RemoveEntryList(&RxContext->ContextListEntry);
2280 
2281             /* If that was the last active context, save the stop context */
2282             if (InterlockedExchangeAdd((volatile LONG *)&RxDeviceObject->NumberOfActiveContexts, -1) == 0)
2283             {
2284                 if (RxDeviceObject->StartStopContext.pStopContext != NULL)
2285                 {
2286                     StopContext = RxDeviceObject->StartStopContext.pStopContext;
2287                 }
2288             }
2289         }
2290     }
2291     KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
2292 
2293     /* Now, deal with what can be done without spinlock held */
2294     if (RefCount == 0)
2295     {
2296         /* Refcount shouldn't have changed */
2297         ASSERT(RxContext->ReferenceCount == 0);
2298         /* Reset everything that can be */
2299         RxPrepareContextForReuse(RxContext);
2300 
2301 #ifdef RDBSS_TRACKER
2302         ASSERT(RxContext->AcquireReleaseFcbTrackerX == 0);
2303 #endif
2304         /* If that was the last active, set the event */
2305         if (StopContext != NULL)
2306         {
2307             StopContext->Flags &= ~RX_CONTEXT_FLAG_RECURSIVE_CALL;
2308             KeSetEvent(&StopContext->SyncEvent, IO_NO_INCREMENT, FALSE);
2309         }
2310 
2311 #if DBG
2312         /* Is ShadowCrit still owned? Shouldn't happen! */
2313         if (RxContext->ShadowCritOwner != 0)
2314         {
2315             DPRINT1("ShadowCritOwner not null! %p\n", (PVOID)RxContext->ShadowCritOwner);
2316             ASSERT(FALSE);
2317         }
2318 #endif
2319 
2320         /* If it was allocated, free it */
2321         if (Allocated)
2322         {
2323             ExFreeToNPagedLookasideList(&RxContextLookasideList, RxContext);
2324         }
2325     }
2326 }
2327 
2328 VOID
2329 NTAPI
2330 RxDispatchChangeBufferingStateRequests(
2331     PVOID Context)
2332 {
2333     UNIMPLEMENTED;
2334 }
2335 
2336 /*
2337  * @implemented
2338  */
2339 NTSTATUS
2340 NTAPI
2341 RxDispatchToWorkerThread(
2342     IN  PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
2343     IN  WORK_QUEUE_TYPE WorkQueueType,
2344     IN  PRX_WORKERTHREAD_ROUTINE Routine,
2345     IN PVOID pContext)
2346 {
2347     NTSTATUS Status;
2348     PRX_WORK_DISPATCH_ITEM DispatchItem;
2349 
2350     /* Allocate a bit of context */
2351     DispatchItem = RxAllocatePoolWithTag(PagedPool, sizeof(RX_WORK_DISPATCH_ITEM), RX_WORKQ_POOLTAG);
2352     if (DispatchItem == NULL)
2353     {
2354         return STATUS_INSUFFICIENT_RESOURCES;
2355     }
2356 
2357     /* Set all the routines, the one our dispatcher will call, the one ntoskrnl will call */
2358     DispatchItem->DispatchRoutine = Routine;
2359     DispatchItem->DispatchRoutineParameter = pContext;
2360     DispatchItem->WorkQueueItem.WorkerRoutine = RxWorkItemDispatcher;
2361     DispatchItem->WorkQueueItem.Parameter = DispatchItem;
2362 
2363     /* Insert item */
2364     Status = RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, &DispatchItem->WorkQueueItem);
2365     if (!NT_SUCCESS(Status))
2366     {
2367         RxFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
2368         DPRINT1("RxInsertWorkQueueItem failed! Queue: %ld, Routine: %p, Context: %p, Status: %lx\n", WorkQueueType, Routine, pContext, Status);
2369     }
2370 
2371     DPRINT("Dispatching: %p, %p\n", Routine, pContext);
2372 
2373     return Status;
2374 }
2375 
2376 /*
2377  * @implemented
2378  */
2379 VOID
2380 RxExclusivePrefixTableLockToShared(
2381     PRX_PREFIX_TABLE Table)
2382 {
2383     PAGED_CODE();
2384 
2385     ExConvertExclusiveToSharedLite(&Table->TableLock);
2386 }
2387 
2388 /*
2389  * @implemented
2390  */
2391 VOID
2392 RxExtractServerName(
2393     IN PUNICODE_STRING FilePathName,
2394     OUT PUNICODE_STRING SrvCallName,
2395     OUT PUNICODE_STRING RestOfName)
2396 {
2397     USHORT i, Length;
2398 
2399     PAGED_CODE();
2400 
2401     ASSERT(SrvCallName != NULL);
2402 
2403     /* SrvCall name will start from the begin up to the first separator */
2404     SrvCallName->Buffer = FilePathName->Buffer;
2405     for (i = 1; i < FilePathName->Length / sizeof(WCHAR); ++i)
2406     {
2407         if (FilePathName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR)
2408         {
2409             break;
2410         }
2411     }
2412 
2413     /* Compute length */
2414     Length = (USHORT)((ULONG_PTR)&FilePathName->Buffer[i] - (ULONG_PTR)FilePathName->Buffer);
2415     SrvCallName->MaximumLength = Length;
2416     SrvCallName->Length = Length;
2417 
2418     /* Return the rest if asked */
2419     if (RestOfName != NULL)
2420     {
2421         Length = (USHORT)((ULONG_PTR)&FilePathName->Buffer[FilePathName->Length / sizeof(WCHAR)] - (ULONG_PTR)FilePathName->Buffer[i]);
2422         RestOfName->Buffer = &FilePathName->Buffer[i];
2423         RestOfName->MaximumLength = Length;
2424         RestOfName->Length = Length;
2425     }
2426 }
2427 
2428 /*
2429  * @implemented
2430  */
2431 NTSTATUS
2432 RxFcbTableInsertFcb(
2433     IN OUT PRX_FCB_TABLE FcbTable,
2434     IN OUT PFCB Fcb)
2435 {
2436     PAGED_CODE();
2437 
2438     /* We deal with the table, make sure it's locked */
2439     ASSERT(RxIsFcbTableLockExclusive(FcbTable));
2440 
2441     /* Compute the hash */
2442     Fcb->FcbTableEntry.HashValue = RxTableComputePathHashValue(&Fcb->FcbTableEntry.Path);
2443 
2444     RxReferenceNetFcb(Fcb);
2445 
2446     /* If no length, it will be our null entry */
2447     if (Fcb->FcbTableEntry.Path.Length == 0)
2448     {
2449         FcbTable->TableEntryForNull = &Fcb->FcbTableEntry;
2450     }
2451     /* Otherwise, insert in the appropriate bucket */
2452     else
2453     {
2454         InsertTailList(FCB_HASH_BUCKET(FcbTable, Fcb->FcbTableEntry.HashValue),
2455                        &Fcb->FcbTableEntry.HashLinks);
2456     }
2457 
2458     /* Propagate the change by incrementing the version number */
2459     InterlockedIncrement((volatile long *)&FcbTable->Version);
2460 
2461     return STATUS_SUCCESS;
2462 }
2463 
2464 /*
2465  * @implemented
2466  */
2467 PFCB
2468 RxFcbTableLookupFcb(
2469     IN  PRX_FCB_TABLE FcbTable,
2470     IN  PUNICODE_STRING Path)
2471 {
2472     PFCB Fcb;
2473     PRX_FCB_TABLE_ENTRY TableEntry;
2474 
2475     PAGED_CODE();
2476 
2477     /* No path - easy, that's null entry */
2478     if (Path == NULL)
2479     {
2480         TableEntry = FcbTable->TableEntryForNull;
2481     }
2482     else
2483     {
2484         ULONG Hash;
2485         PLIST_ENTRY HashBucket, ListEntry;
2486 
2487         /* Otherwise, compute the hash value and find the associated bucket */
2488         Hash = RxTableComputePathHashValue(Path);
2489         HashBucket = FCB_HASH_BUCKET(FcbTable, Hash);
2490         /* If the bucket is empty, it means there's no entry yet */
2491         if (IsListEmpty(HashBucket))
2492         {
2493             TableEntry = NULL;
2494         }
2495         else
2496         {
2497             /* Otherwise, browse all the entry */
2498             for (ListEntry = HashBucket->Flink;
2499                  ListEntry != HashBucket;
2500                  ListEntry = ListEntry->Flink)
2501             {
2502                 TableEntry = CONTAINING_RECORD(ListEntry, RX_FCB_TABLE_ENTRY, HashLinks);
2503                 InterlockedIncrement(&FcbTable->Compares);
2504 
2505                 /* If entry hash and string are equal, thatt's the one! */
2506                 if (TableEntry->HashValue == Hash &&
2507                     TableEntry->Path.Length == Path->Length &&
2508                     RtlEqualUnicodeString(Path, &TableEntry->Path, FcbTable->CaseInsensitiveMatch))
2509                 {
2510                     break;
2511                 }
2512             }
2513 
2514             /* We reached the end? Not found */
2515             if (ListEntry == HashBucket)
2516             {
2517                 TableEntry = NULL;
2518             }
2519         }
2520     }
2521 
2522     InterlockedIncrement(&FcbTable->Lookups);
2523 
2524     /* If table entry isn't null, return the FCB */
2525     if (TableEntry != NULL)
2526     {
2527         Fcb = CONTAINING_RECORD(TableEntry, FCB, FcbTableEntry);
2528         RxReferenceNetFcb(Fcb);
2529     }
2530     else
2531     {
2532         Fcb = NULL;
2533         InterlockedIncrement(&FcbTable->FailedLookups);
2534     }
2535 
2536     return Fcb;
2537 }
2538 
2539 /*
2540  * @implemented
2541  */
2542 NTSTATUS
2543 RxFcbTableRemoveFcb(
2544     IN OUT PRX_FCB_TABLE FcbTable,
2545     IN OUT PFCB Fcb)
2546 {
2547     PAGED_CODE();
2548 
2549     ASSERT(RxIsPrefixTableLockExclusive(FcbTable));
2550 
2551     /* If no path, then remove entry for null */
2552     if (Fcb->FcbTableEntry.Path.Length == 0)
2553     {
2554         FcbTable->TableEntryForNull = NULL;
2555     }
2556     /* Otherwise, remove from the bucket */
2557     else
2558     {
2559         RemoveEntryList(&Fcb->FcbTableEntry.HashLinks);
2560     }
2561 
2562     /* Reset its list entry */
2563     InitializeListHead(&Fcb->FcbTableEntry.HashLinks);
2564 
2565     /* Propagate the change by incrementing the version number */
2566     InterlockedIncrement((volatile long *)&FcbTable->Version);
2567 
2568     return STATUS_SUCCESS;
2569 }
2570 
2571 /*
2572  * @implemented
2573  */
2574 NTSTATUS
2575 NTAPI
2576 RxFinalizeConnection(
2577     IN OUT PNET_ROOT NetRoot,
2578     IN OUT PV_NET_ROOT VNetRoot OPTIONAL,
2579     IN LOGICAL ForceFilesClosed)
2580 {
2581     NTSTATUS Status;
2582     PRX_PREFIX_TABLE PrefixTable;
2583     ULONG UncleanAny, UncleanDir;
2584     LONG FilesOpen, AdditionalRef;
2585     BOOLEAN PrefixLocked, FcbTableLocked, ForceClose;
2586 
2587     PAGED_CODE();
2588 
2589     ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
2590 
2591     /* Get a BOOLEAN out of LOGICAL
2592      * -1 is like FALSE but also drops extra V_NET_ROOT reference in case of failure
2593      */
2594     ForceClose = (ForceFilesClosed == TRUE ? TRUE : FALSE);
2595 
2596     /* First, delete any notification change */
2597     Status = RxCancelNotifyChangeDirectoryRequestsForVNetRoot(VNetRoot, ForceClose);
2598     /* If it failed, continue if forced */
2599     if (Status != STATUS_SUCCESS && !ForceFilesClosed)
2600     {
2601         return Status;
2602     }
2603     /* Reset status, in case notification deletion failed */
2604     Status = STATUS_SUCCESS;
2605 
2606     PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
2607 
2608     PrefixLocked = FALSE;
2609     FcbTableLocked = FALSE;
2610     FilesOpen = 0;
2611     AdditionalRef = 0;
2612     UncleanAny = 0;
2613     UncleanDir = 0;
2614     _SEH2_TRY
2615     {
2616         RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
2617         PrefixLocked = TRUE;
2618 
2619         RxReferenceNetRoot(NetRoot);
2620 
2621         RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
2622         FcbTableLocked = TRUE;
2623 
2624         /* If our V_NET_ROOT wasn't finalized yet, proceed! */
2625         if (!VNetRoot->ConnectionFinalizationDone)
2626         {
2627             USHORT Bucket;
2628             PRX_FCB_TABLE FcbTable;
2629 
2630             DPRINT("Finalizing connection %p: %wZ\n", NetRoot, &NetRoot->PrefixEntry.Prefix);
2631 
2632             /* We'll browse all its associated FCB to check whether they're open/orphaned */
2633             FcbTable = &NetRoot->FcbTable;
2634             for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
2635             {
2636                 PLIST_ENTRY BucketList, Entry;
2637 
2638                 BucketList = &FcbTable->HashBuckets[Bucket];
2639                 Entry = BucketList->Flink;
2640                 while (Entry != BucketList)
2641                 {
2642                     PFCB Fcb;
2643 
2644                     Fcb = CONTAINING_RECORD(Entry, FCB, FcbTableEntry.HashLinks);
2645                     Entry = Entry->Flink;
2646 
2647                     /* FCB for this connection, go ahead */
2648                     if (Fcb->VNetRoot == VNetRoot)
2649                     {
2650                         /* It's still open, and no force? Fail and keep track */
2651                         if (Fcb->UncleanCount > 0 && !ForceClose)
2652                         {
2653                             Status = STATUS_CONNECTION_IN_USE;
2654                             if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_DIRECTORY)
2655                             {
2656                                 ++UncleanDir;
2657                             }
2658                             else
2659                             {
2660                                 ++UncleanAny;
2661                             }
2662                         }
2663                         else
2664                         {
2665                             /* Else, force purge */
2666                             ASSERT(NodeTypeIsFcb(Fcb));
2667 
2668                             Status = RxAcquireExclusiveFcb(NULL, Fcb);
2669                             ASSERT(Status == STATUS_SUCCESS);
2670 
2671                             ClearFlag(Fcb->FcbState, FCB_STATE_COLLAPSING_ENABLED);
2672 
2673                             RxScavengeRelatedFobxs(Fcb);
2674                             RxPurgeFcb(Fcb);
2675 
2676                             /* We don't need to release FCB lock, FCB finalize will take care of it */
2677                         }
2678                     }
2679                 }
2680             }
2681 
2682             /* No files left, our V_NET_ROOT is finalized */
2683             if (VNetRoot->NumberOfFobxs == 0)
2684             {
2685                 VNetRoot->ConnectionFinalizationDone = TRUE;
2686             }
2687         }
2688 
2689         /* Keep Number of open files and track of the extra reference */
2690         FilesOpen = VNetRoot->NumberOfFobxs;
2691         AdditionalRef = VNetRoot->AdditionalReferenceForDeleteFsctlTaken;
2692         /* If force close, caller doesn't want to keep connection alive
2693          * and wants it totally close, so drop the V_NET_ROOT too
2694          */
2695         if (ForceClose)
2696         {
2697             RxFinalizeVNetRoot(VNetRoot, FALSE, TRUE);
2698         }
2699     }
2700     _SEH2_FINALLY
2701     {
2702         /* Release what was acquired */
2703         if (FcbTableLocked)
2704         {
2705             RxReleaseFcbTableLock(&NetRoot->FcbTable);
2706         }
2707 
2708         /* If close is forced, only fix status if there are open files */
2709         if (ForceClose)
2710         {
2711             if (Status != STATUS_SUCCESS && UncleanAny != 0)
2712             {
2713                 Status = STATUS_FILES_OPEN;
2714             }
2715         }
2716         /* Else, fix status and fail closing if there are open files */
2717         else
2718         {
2719             if ((Status != STATUS_SUCCESS && UncleanAny != 0) || FilesOpen > 0)
2720             {
2721                 Status = STATUS_FILES_OPEN;
2722             }
2723         }
2724 
2725         DPRINT("UncleanAny: %ld, UncleanDir: %ld, FilesOpen: %ld\n", UncleanAny, UncleanDir, FilesOpen);
2726 
2727         /* If we're are asked to remove the extra ref, or if closing was a success, do it;
2728          * only if it was still referenced!
2729          */
2730         if ((ForceFilesClosed == 0xFF || Status == STATUS_SUCCESS) && AdditionalRef != 0)
2731         {
2732             VNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0;
2733             RxDereferenceVNetRoot(VNetRoot, LHS_ExclusiveLockHeld);
2734         }
2735 
2736         if (PrefixLocked)
2737         {
2738             RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
2739             RxReleasePrefixTableLock(PrefixTable);
2740         }
2741     }
2742     _SEH2_END;
2743 
2744     return Status;
2745 }
2746 
2747 /*
2748  * @implemented
2749  */
2750 VOID
2751 RxFinalizeFcbTable(
2752     IN OUT PRX_FCB_TABLE FcbTable)
2753 {
2754     USHORT Bucket;
2755 
2756     PAGED_CODE();
2757 
2758     /* Just delete the lock */
2759     ExDeleteResourceLite(&FcbTable->TableLock);
2760 
2761     /* And make sure (checked) that the table is really empty... */
2762     for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
2763     {
2764         ASSERT(IsListEmpty(&FcbTable->HashBuckets[Bucket]));
2765     }
2766 }
2767 
2768 /*
2769  * @implemented
2770  */
2771 BOOLEAN
2772 RxFinalizeNetFcb(
2773     OUT PFCB ThisFcb,
2774     IN BOOLEAN RecursiveFinalize,
2775     IN BOOLEAN ForceFinalize,
2776     IN LONG ReferenceCount)
2777 {
2778     PAGED_CODE();
2779 
2780     DPRINT("RxFinalizeNetFcb(%p, %d, %d, %d)\n", ThisFcb, RecursiveFinalize, ForceFinalize, ReferenceCount);
2781     DPRINT("Finalize: %wZ\n", &ThisFcb->FcbTableEntry.Path);
2782 
2783     /* Make sure we have an exclusively acquired FCB */
2784     ASSERT_CORRECT_FCB_STRUCTURE(ThisFcb);
2785     ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
2786 
2787     /* We shouldn't force finalization... */
2788     ASSERT(!ForceFinalize);
2789 
2790     /* If recurisve, finalize all the associated SRV_OPEN */
2791     if (RecursiveFinalize)
2792     {
2793         PLIST_ENTRY ListEntry;
2794 
2795         for (ListEntry = ThisFcb->SrvOpenList.Flink;
2796              ListEntry != &ThisFcb->SrvOpenList;
2797              ListEntry = ListEntry->Flink)
2798         {
2799             PSRV_OPEN SrvOpen;
2800 
2801             SrvOpen = CONTAINING_RECORD(ListEntry, SRV_OPEN, SrvOpenQLinks);
2802             RxFinalizeSrvOpen(SrvOpen, TRUE, ForceFinalize);
2803         }
2804     }
2805     /* If FCB is still in use, that's over */
2806     else
2807     {
2808         if (ThisFcb->OpenCount != 0 || ThisFcb->UncleanCount != 0)
2809         {
2810             ASSERT(ReferenceCount > 0);
2811 
2812             return FALSE;
2813         }
2814     }
2815 
2816     ASSERT(ReferenceCount >= 1);
2817 
2818     /* If FCB is still referenced, that's over - unless you force it and want to BSOD somewhere */
2819     if (ReferenceCount != 1 && !ForceFinalize)
2820     {
2821         return FALSE;
2822     }
2823 
2824     ASSERT(ForceFinalize || ((ThisFcb->OpenCount == 0) && (ThisFcb->UncleanCount == 0)));
2825 
2826     DPRINT("Finalizing FCB open: %d (%d)\n", ThisFcb->OpenCount, ForceFinalize);
2827 
2828     /* If finalization was not already initiated, go ahead */
2829     if (!ThisFcb->UpperFinalizationDone)
2830     {
2831         /* Free any FCB_LOCK */
2832         if (NodeType(ThisFcb) == RDBSS_NTC_STORAGE_TYPE_FILE)
2833         {
2834             FsRtlUninitializeFileLock(&ThisFcb->Specific.Fcb.FileLock);
2835 
2836             while (ThisFcb->BufferedLocks.List != NULL)
2837             {
2838                 PFCB_LOCK Entry;
2839 
2840                 Entry = ThisFcb->BufferedLocks.List;
2841                 ThisFcb->BufferedLocks.List = Entry->Next;
2842 
2843                 RxFreePool(Entry);
2844             }
2845         }
2846 
2847         /* If not orphaned, it still has a NET_ROOT and potentially is still in a table */
2848         if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_ORPHANED))
2849         {
2850             PNET_ROOT NetRoot;
2851 
2852             NetRoot = (PNET_ROOT)ThisFcb->pNetRoot;
2853 
2854             ASSERT(RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
2855             /* So, remove it */
2856             if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED))
2857             {
2858                 RxFcbTableRemoveFcb(&NetRoot->FcbTable, ThisFcb);
2859             }
2860         }
2861 
2862         ThisFcb->UpperFinalizationDone = TRUE;
2863     }
2864 
2865     ASSERT(ReferenceCount >= 1);
2866 
2867     /* Even if forced, don't allow broken free */
2868     if (ReferenceCount != 1)
2869     {
2870         return FALSE;
2871     }
2872 
2873     /* Now, release everything */
2874     if (ThisFcb->pBufferingStateChangeCompletedEvent != NULL)
2875     {
2876         RxFreePool(ThisFcb->pBufferingStateChangeCompletedEvent);
2877     }
2878 
2879     if (ThisFcb->MRxDispatch != NULL)
2880     {
2881         ThisFcb->MRxDispatch->MRxDeallocateForFcb(RX_GET_MRX_FCB(ThisFcb));
2882     }
2883 
2884     ExDeleteResourceLite(ThisFcb->BufferedLocks.Resource);
2885     ExDeleteResourceLite(ThisFcb->Header.Resource);
2886     ExDeleteResourceLite(ThisFcb->Header.PagingIoResource);
2887 
2888     InterlockedDecrement((volatile long *)&ThisFcb->pNetRoot->NumberOfFcbs);
2889     RxDereferenceVNetRoot(ThisFcb->VNetRoot, LHS_LockNotHeld);
2890 
2891     ASSERT(IsListEmpty(&ThisFcb->FcbTableEntry.HashLinks));
2892     ASSERT(!ThisFcb->fMiniInited);
2893 
2894     /* And free the object */
2895     RxFreeFcbObject(ThisFcb);
2896 
2897     return TRUE;
2898 }
2899 
2900 /*
2901  * @implemented
2902  */
2903 BOOLEAN
2904 RxFinalizeNetFobx(
2905     _Out_ PFOBX ThisFobx,
2906     _In_ BOOLEAN RecursiveFinalize,
2907     _In_ BOOLEAN ForceFinalize)
2908 {
2909     PFCB Fcb;
2910     PSRV_OPEN SrvOpen;
2911 
2912     PAGED_CODE();
2913 
2914     ASSERT(NodeType(ThisFobx) == RDBSS_NTC_FOBX);
2915 
2916     /* Only finalize if forced or if there's no ref left */
2917     if (ThisFobx->NodeReferenceCount != 0 &&
2918         !ForceFinalize)
2919     {
2920         return FALSE;
2921     }
2922 
2923     DPRINT("Finalize Fobx: %p (with %d ref), forced: %d\n", ThisFobx, ThisFobx->NodeReferenceCount, ForceFinalize);
2924 
2925     SrvOpen = ThisFobx->SrvOpen;
2926     Fcb = SrvOpen->Fcb;
2927     /* If it wasn't finalized yet, do it */
2928     if (!ThisFobx->UpperFinalizationDone)
2929     {
2930         ASSERT(NodeType(SrvOpen->Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB);
2931         ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
2932 
2933         /* Remove it from the SRV_OPEN */
2934         RemoveEntryList(&ThisFobx->FobxQLinks);
2935 
2936         /* If we were used to browse a directory, free the query buffer */
2937         if (BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_FREE_UNICODE))
2938         {
2939             RxFreePoolWithTag(ThisFobx->UnicodeQueryTemplate.Buffer, RX_DIRCTL_POOLTAG);
2940         }
2941 
2942         /* Notify the mini-rdr */
2943         if (Fcb->MRxDispatch != NULL && Fcb->MRxDispatch->MRxDeallocateForFobx != NULL)
2944         {
2945             Fcb->MRxDispatch->MRxDeallocateForFobx((PMRX_FOBX)ThisFobx);
2946         }
2947 
2948         /* If the SRV_OPEN wasn't closed yet, do it */
2949         if (!BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED))
2950         {
2951             NTSTATUS Status;
2952 
2953             Status = RxCloseAssociatedSrvOpen(ThisFobx, FALSE);
2954             DPRINT("Closing SRV_OPEN %p for %p: %x\n", SrvOpen, ThisFobx, Status);
2955         }
2956 
2957         /* Finalization done */
2958         ThisFobx->UpperFinalizationDone = TRUE;
2959     }
2960 
2961     /* If we're still referenced, don't go any further! */
2962     if (ThisFobx->NodeReferenceCount != 0)
2963     {
2964         return FALSE;
2965     }
2966 
2967     /* At that point, everything should be closed */
2968     ASSERT(IsListEmpty(&ThisFobx->ClosePendingList));
2969 
2970     /* Was the FOBX allocated with another object?
2971      * If so, mark the buffer free in said object
2972      */
2973     if (ThisFobx == Fcb->InternalFobx)
2974     {
2975         ClearFlag(Fcb->FcbState, FCB_STATE_FOBX_USED);
2976     }
2977     else if (ThisFobx == SrvOpen->InternalFobx)
2978     {
2979         ClearFlag(SrvOpen->Flags, SRVOPEN_FLAG_FOBX_USED);
2980     }
2981 
2982     ThisFobx->pSrvOpen = NULL;
2983 
2984     /* A FOBX less */
2985     InterlockedDecrement((volatile long *)&SrvOpen->pVNetRoot->NumberOfFobxs);
2986 
2987     RxDereferenceSrvOpen(SrvOpen, LHS_ExclusiveLockHeld);
2988 
2989     /* If it wasn't allocated with another object, free the FOBX */
2990     if (!BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_ENCLOSED_ALLOCATED))
2991     {
2992         RxFreeFcbObject(ThisFobx);
2993     }
2994 
2995     return TRUE;
2996 }
2997 
2998 /*
2999  * @implemented
3000  */
3001 BOOLEAN
3002 RxFinalizeNetRoot(
3003     OUT PNET_ROOT ThisNetRoot,
3004     IN BOOLEAN RecursiveFinalize,
3005     IN BOOLEAN ForceFinalize)
3006 {
3007     PSRV_CALL SrvCall;
3008     PRX_FCB_TABLE FcbTable;
3009     PRX_PREFIX_TABLE PrefixTable;
3010 
3011     PAGED_CODE();
3012 
3013     ASSERT(NodeType(ThisNetRoot) == RDBSS_NTC_NETROOT);
3014 
3015     PrefixTable = ThisNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
3016     ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
3017 
3018     /* If sme finalization is already ongoing, leave */
3019     if (BooleanFlagOn(ThisNetRoot->Flags, NETROOT_FLAG_FINALIZATION_IN_PROGRESS))
3020     {
3021         return FALSE;
3022     }
3023 
3024     /* Mark we're finalizing */
3025     SetFlag(ThisNetRoot->Flags, NETROOT_FLAG_FINALIZATION_IN_PROGRESS);
3026 
3027     FcbTable = &ThisNetRoot->FcbTable;
3028     /* Did caller asked us to finalize any associated FCB? */
3029     if (RecursiveFinalize)
3030     {
3031         USHORT Bucket;
3032 
3033         /* Browse all the FCBs in our FCB table */
3034         RxAcquireFcbTableLockExclusive(FcbTable, TRUE);
3035         for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
3036         {
3037             PLIST_ENTRY HashBucket, ListEntry;
3038 
3039             HashBucket = &FcbTable->HashBuckets[Bucket];
3040             ListEntry = HashBucket->Flink;
3041             while (ListEntry != HashBucket)
3042             {
3043                 PFCB Fcb;
3044 
3045                 Fcb = CONTAINING_RECORD(ListEntry, FCB, FcbTableEntry.HashLinks);
3046                 ASSERT(NodeTypeIsFcb(Fcb));
3047 
3048                 ListEntry = ListEntry->Flink;
3049 
3050                 /* If the FCB isn't orphaned, then, it's time to purge it */
3051                 if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_ORPHANED))
3052                 {
3053                     NTSTATUS Status;
3054 
3055                     Status = RxAcquireExclusiveFcb(NULL, Fcb);
3056                     ASSERT(Status == STATUS_SUCCESS);
3057                     RxPurgeFcb(Fcb);
3058                 }
3059             }
3060         }
3061         RxReleaseFcbTableLock(FcbTable);
3062     }
3063 
3064     /* Only finalize if forced or if there's a single ref left */
3065     if (ThisNetRoot->NodeReferenceCount != 1 && !ForceFinalize)
3066     {
3067         return FALSE;
3068     }
3069 
3070     DPRINT("Finalizing NetRoot %p for %wZ\n", ThisNetRoot, &ThisNetRoot->PrefixEntry.Prefix);
3071 
3072     /* If we're still referenced, don't go any further! */
3073     if (ThisNetRoot->NodeReferenceCount != 1)
3074     {
3075         return FALSE;
3076     }
3077 
3078     /* Finalize the FCB table (and make sure it's empty!) */
3079     RxFinalizeFcbTable(FcbTable);
3080 
3081     /* If name wasn't remove already, do it now */
3082     if (!BooleanFlagOn(ThisNetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED))
3083     {
3084         RxRemovePrefixTableEntry(PrefixTable, &ThisNetRoot->PrefixEntry);
3085     }
3086 
3087     /* Delete the object */
3088     SrvCall = (PSRV_CALL)ThisNetRoot->pSrvCall;
3089     RxFreeObject(ThisNetRoot);
3090 
3091     /* And dereference the associated SRV_CALL */
3092     if (SrvCall != NULL)
3093     {
3094         RxDereferenceSrvCall(SrvCall, LHS_ExclusiveLockHeld);
3095     }
3096 
3097     return TRUE;
3098 }
3099 
3100 /*
3101  * @implemented
3102  */
3103 BOOLEAN
3104 RxFinalizeSrvCall(
3105     OUT PSRV_CALL ThisSrvCall,
3106     IN BOOLEAN RecursiveFinalize,
3107     IN BOOLEAN ForceFinalize)
3108 {
3109     PRX_PREFIX_TABLE PrefixTable;
3110 
3111     PAGED_CODE();
3112 
3113     ASSERT(NodeType(ThisSrvCall) == RDBSS_NTC_SRVCALL);
3114 
3115     PrefixTable = ThisSrvCall->RxDeviceObject->pRxNetNameTable;
3116     ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
3117 
3118     /* Only finalize if forced or if there's a single ref left */
3119     if (ThisSrvCall->NodeReferenceCount != 1 &&
3120         !ForceFinalize)
3121     {
3122         return FALSE;
3123     }
3124 
3125     DPRINT("Finalizing SrvCall %p for %wZ\n", ThisSrvCall, &ThisSrvCall->PrefixEntry.Prefix);
3126 
3127     /* If it wasn't finalized yet, do it */
3128     if (!ThisSrvCall->UpperFinalizationDone)
3129     {
3130         BOOLEAN WillFree;
3131 
3132         /* Remove ourselves from prefix table */
3133         RxRemovePrefixTableEntry(PrefixTable, &ThisSrvCall->PrefixEntry);
3134 
3135         /* Remember our third arg, in case we get queued for later execution */
3136         if (ForceFinalize)
3137         {
3138             SetFlag(ThisSrvCall->Flags, SRVCALL_FLAG_FORCE_FINALIZED);
3139         }
3140 
3141         /* And done */
3142         ThisSrvCall->UpperFinalizationDone = TRUE;
3143 
3144         /* Would defered execution free the object? */
3145         WillFree = (ThisSrvCall->NodeReferenceCount == 1);
3146 
3147         /* If we have a device object */
3148         if (ThisSrvCall->RxDeviceObject != NULL)
3149         {
3150             NTSTATUS Status;
3151 
3152             /* If we're not executing in the RDBSS thread, queue for execution within the thread */
3153             if (RxGetRDBSSProcess() != IoGetCurrentProcess())
3154             {
3155                 /* Extra ref, as usual */
3156                 InterlockedIncrement((volatile long *)&ThisSrvCall->NodeReferenceCount);
3157                 /* And dispatch */
3158                 RxDispatchToWorkerThread(ThisSrvCall->RxDeviceObject, DelayedWorkQueue, RxpDestroySrvCall, ThisSrvCall);
3159 
3160                 /* Return to the caller, in advance, whether we're freeing the object or not */
3161                 return WillFree;
3162             }
3163 
3164             /* If in the right thread already, call the mini-rdr */
3165             MINIRDR_CALL_THROUGH(Status, ThisSrvCall->RxDeviceObject->Dispatch,
3166                                  MRxFinalizeSrvCall, ((PMRX_SRV_CALL)ThisSrvCall, ForceFinalize));
3167             (void)Status;
3168         }
3169     }
3170 
3171     /* If we're still referenced, don't go any further! */
3172     if (ThisSrvCall->NodeReferenceCount != 1)
3173     {
3174         return FALSE;
3175     }
3176 
3177     /* Don't leak */
3178     if (ThisSrvCall->pDomainName != NULL)
3179     {
3180         RxFreePool(ThisSrvCall->pDomainName);
3181     }
3182 
3183     /* And free! */
3184     RxTearDownBufferingManager(ThisSrvCall);
3185     RxFreeObject(ThisSrvCall);
3186 
3187     return TRUE;
3188 }
3189 
3190 /*
3191  * @implemented
3192  */
3193 BOOLEAN
3194 RxFinalizeSrvOpen(
3195     OUT PSRV_OPEN ThisSrvOpen,
3196     IN BOOLEAN RecursiveFinalize,
3197     IN BOOLEAN ForceFinalize)
3198 {
3199     PFCB Fcb;
3200 
3201     PAGED_CODE();
3202 
3203     /* We have to have a SRV_OPEN */
3204     ASSERT(NodeType(ThisSrvOpen) == RDBSS_NTC_SRVOPEN);
3205 
3206     /* If that's a recursive finalization, finalize any related FOBX */
3207     if (RecursiveFinalize)
3208     {
3209         PLIST_ENTRY ListEntry;
3210 
3211         ListEntry = ThisSrvOpen->FobxList.Flink;
3212         while (ListEntry != &ThisSrvOpen->FobxList)
3213         {
3214             PFOBX Fobx;
3215 
3216             Fobx = CONTAINING_RECORD(ListEntry, FOBX, FobxQLinks);
3217             ListEntry = ListEntry->Flink;
3218             RxFinalizeNetFobx(Fobx, TRUE, ForceFinalize);
3219         }
3220     }
3221 
3222     /* If we have still references, don't finalize unless forced */
3223     if (ThisSrvOpen->NodeReferenceCount != 0 &&
3224         !ForceFinalize)
3225     {
3226         return FALSE;
3227     }
3228 
3229     DPRINT("Finalize SRV_OPEN: %p (with %d ref), forced: %d\n", ThisSrvOpen, ThisSrvOpen->NodeReferenceCount, ForceFinalize);
3230 
3231     /* Only finalize if closed, or if it wasn't already done and SRV_OPEN is in a bad shape */
3232     Fcb = (PFCB)ThisSrvOpen->pFcb;
3233     if ((!ThisSrvOpen->UpperFinalizationDone && ThisSrvOpen->Condition != Condition_Good) ||
3234         BooleanFlagOn(ThisSrvOpen->Flags, SRVOPEN_FLAG_CLOSED))
3235     {
3236         PV_NET_ROOT VNetRoot;
3237 
3238         /* Associated FCB can't be fake one */
3239         ASSERT(NodeType(Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB);
3240         ASSERT(RxIsFcbAcquiredExclusive (Fcb));
3241 
3242         /* Purge any pending operation */
3243         RxPurgeChangeBufferingStateRequestsForSrvOpen(ThisSrvOpen);
3244 
3245         /* If the FCB wasn't orphaned, inform the mini-rdr about close */
3246         if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_ORPHANED))
3247         {
3248             NTSTATUS Status;
3249 
3250             MINIRDR_CALL_THROUGH(Status, Fcb->MRxDispatch, MRxForceClosed, ((PMRX_SRV_OPEN)ThisSrvOpen));
3251             (void)Status;
3252         }
3253 
3254         /* Remove ourselves from the FCB */
3255         RemoveEntryList(&ThisSrvOpen->SrvOpenQLinks);
3256         InitializeListHead(&ThisSrvOpen->SrvOpenQLinks);
3257         ++Fcb->SrvOpenListVersion;
3258 
3259         /* If we have a V_NET_ROOT, dereference it */
3260         VNetRoot = (PV_NET_ROOT)ThisSrvOpen->pVNetRoot;
3261         if (VNetRoot != NULL)
3262         {
3263             InterlockedDecrement((volatile long *)&VNetRoot->pNetRoot->NumberOfSrvOpens);
3264             RxDereferenceVNetRoot(VNetRoot, LHS_LockNotHeld);
3265             ThisSrvOpen->pVNetRoot = NULL;
3266         }
3267 
3268         /* Finalization done */
3269         ThisSrvOpen->UpperFinalizationDone = TRUE;
3270     }
3271 
3272     /* Don't free memory if still referenced */
3273     if (ThisSrvOpen->NodeReferenceCount != 0)
3274     {
3275         return FALSE;
3276     }
3277 
3278     /* No key association left */
3279     ASSERT(IsListEmpty(&ThisSrvOpen->SrvOpenKeyList));
3280 
3281     /* If we're still in some FCB, remove us */
3282     if (!IsListEmpty(&ThisSrvOpen->SrvOpenQLinks))
3283     {
3284         RemoveEntryList(&ThisSrvOpen->SrvOpenQLinks);
3285     }
3286 
3287     /* If enclosed allocation, mark the memory zone free */
3288     if (BooleanFlagOn(ThisSrvOpen->Flags, SRVOPEN_FLAG_ENCLOSED_ALLOCATED))
3289     {
3290         ClearFlag(Fcb->FcbState, FCB_STATE_SRVOPEN_USED);
3291     }
3292     /* Otherwise, free the memory */
3293     else
3294     {
3295         RxFreeFcbObject(ThisSrvOpen);
3296     }
3297 
3298     RxDereferenceNetFcb(Fcb);
3299 
3300     return TRUE;
3301 }
3302 
3303 /*
3304  * @implemented
3305  */
3306 BOOLEAN
3307 RxFinalizeVNetRoot(
3308     OUT PV_NET_ROOT ThisVNetRoot,
3309     IN BOOLEAN RecursiveFinalize,
3310     IN BOOLEAN ForceFinalize)
3311 {
3312     PNET_ROOT NetRoot;
3313     PRX_PREFIX_TABLE PrefixTable;
3314 
3315     PAGED_CODE();
3316 
3317     ASSERT(NodeType(ThisVNetRoot) == RDBSS_NTC_V_NETROOT);
3318 
3319     PrefixTable = ThisVNetRoot->pNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
3320     ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
3321 
3322     /* Only finalize if forced or if there's a single ref left */
3323     if (ThisVNetRoot->NodeReferenceCount != 1 &&
3324         !ForceFinalize)
3325     {
3326         return FALSE;
3327     }
3328 
3329     DPRINT("Finalizing VNetRoot %p for %wZ\n", ThisVNetRoot, &ThisVNetRoot->PrefixEntry.Prefix);
3330 
3331     NetRoot = (PNET_ROOT)ThisVNetRoot->pNetRoot;
3332     /* If it wasn't finalized yet, do it */
3333     if (!ThisVNetRoot->UpperFinalizationDone)
3334     {
3335         ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
3336 
3337         /* Reference the NetRoot so that it doesn't disappear */
3338         RxReferenceNetRoot(NetRoot);
3339         RxOrphanSrvOpens(ThisVNetRoot);
3340         /* Remove us from the available VNetRoot for NetRoot */
3341         RxRemoveVirtualNetRootFromNetRoot(NetRoot, ThisVNetRoot);
3342         /* Remove extra ref */
3343         RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
3344 
3345         /* Remove ourselves from prefix table */
3346         RxRemovePrefixTableEntry(PrefixTable, &ThisVNetRoot->PrefixEntry);
3347 
3348         /* Finalization done */
3349         ThisVNetRoot->UpperFinalizationDone = TRUE;
3350     }
3351 
3352     /* If we're still referenced, don't go any further! */
3353     if (ThisVNetRoot->NodeReferenceCount != 1)
3354     {
3355         return FALSE;
3356     }
3357 
3358     /* If there's an associated device, notify mini-rdr */
3359     if (NetRoot->pSrvCall->RxDeviceObject != NULL)
3360     {
3361         NTSTATUS Status;
3362 
3363         MINIRDR_CALL_THROUGH(Status, NetRoot->pSrvCall->RxDeviceObject->Dispatch,
3364                              MRxFinalizeVNetRoot, ((PMRX_V_NET_ROOT)ThisVNetRoot, FALSE));
3365         (void)Status;
3366     }
3367 
3368     /* Free parameters */
3369     RxUninitializeVNetRootParameters(ThisVNetRoot->pUserName, ThisVNetRoot->pUserDomainName,
3370                                      ThisVNetRoot->pPassword, &ThisVNetRoot->Flags);
3371     /* Dereference our NetRoot, we won't reference it anymore */
3372     RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
3373 
3374     /* And free the object! */
3375     RxFreePoolWithTag(ThisVNetRoot, RX_V_NETROOT_POOLTAG);
3376 
3377     return TRUE;
3378 }
3379 
3380 NTSTATUS
3381 RxFindOrConstructVirtualNetRoot(
3382     IN PRX_CONTEXT RxContext,
3383     IN PUNICODE_STRING CanonicalName,
3384     IN NET_ROOT_TYPE NetRootType,
3385     IN PUNICODE_STRING RemainingName)
3386 {
3387     ULONG Flags;
3388     NTSTATUS Status;
3389     PVOID Container;
3390     BOOLEAN Construct;
3391     PV_NET_ROOT VNetRoot;
3392     RX_CONNECTION_ID ConnectionID;
3393     PRDBSS_DEVICE_OBJECT RxDeviceObject;
3394     LOCK_HOLDING_STATE LockHoldingState;
3395 
3396     PAGED_CODE();
3397 
3398     RxDeviceObject = RxContext->RxDeviceObject;
3399     ASSERT(RxDeviceObject->Dispatch != NULL);
3400     ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
3401 
3402     /* Ask the mini-rdr for connection ID */
3403     ConnectionID.SessionID = 0;
3404     if (RxDeviceObject->Dispatch->MRxGetConnectionId != NULL)
3405     {
3406         Status = RxDeviceObject->Dispatch->MRxGetConnectionId(RxContext, &ConnectionID);
3407         if (!NT_SUCCESS(Status) && Status != STATUS_NOT_IMPLEMENTED)
3408         {
3409             /* mini-rdr is expected not to fail - unless it's not implemented */
3410             DPRINT1("Failed to initialize connection ID\n");
3411             ASSERT(FALSE);
3412         }
3413     }
3414 
3415     RxContext->Create.NetNamePrefixEntry = NULL;
3416 
3417     Status = STATUS_MORE_PROCESSING_REQUIRED;
3418     RxAcquirePrefixTableLockShared(RxDeviceObject->pRxNetNameTable, TRUE);
3419     LockHoldingState = LHS_SharedLockHeld;
3420     Construct = TRUE;
3421     Flags = 0;
3422 
3423     /* We will try twice to find a matching VNetRoot: shared locked and then exlusively locked */
3424     while (TRUE)
3425     {
3426         PNET_ROOT NetRoot;
3427         PV_NET_ROOT SavedVNetRoot;
3428 
3429         /* Look in prefix table */
3430         Container = RxPrefixTableLookupName(RxDeviceObject->pRxNetNameTable, CanonicalName, RemainingName, &ConnectionID);
3431         if (Container != NULL)
3432         {
3433             /* If that's not a VNetRoot, that's a SrvCall, not interesting, loop again */
3434             if (NodeType(Container) != RDBSS_NTC_V_NETROOT)
3435             {
3436                 ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
3437                 RxDereferenceSrvCall(Container, LockHoldingState);
3438             }
3439             else
3440             {
3441                 VNetRoot = Container;
3442                 NetRoot = VNetRoot->NetRoot;
3443 
3444                 /* If the matching VNetRoot isn't in a good shape, there's something wrong - fail */
3445                 if ((NetRoot->Condition != Condition_InTransition && NetRoot->Condition != Condition_Good) ||
3446                     NetRoot->SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
3447                 {
3448                     Status = STATUS_BAD_NETWORK_PATH;
3449                     SavedVNetRoot = NULL;
3450                 }
3451                 else
3452                 {
3453                     LUID LogonId;
3454                     ULONG SessionId;
3455                     PUNICODE_STRING UserName, UserDomain, Password;
3456 
3457                     /* We can reuse if we use same credentials */
3458                     Status = RxInitializeVNetRootParameters(RxContext, &LogonId,
3459                                                             &SessionId, &UserName,
3460                                                             &UserDomain, &Password,
3461                                                             &Flags);
3462                     if (NT_SUCCESS(Status))
3463                     {
3464                         SavedVNetRoot = VNetRoot;
3465                         Status = RxCheckVNetRootCredentials(RxContext, VNetRoot,
3466                                                             &LogonId, UserName,
3467                                                             UserDomain, Password,
3468                                                             Flags);
3469                         if (Status == STATUS_MORE_PROCESSING_REQUIRED)
3470                         {
3471                             PLIST_ENTRY ListEntry;
3472 
3473                             for (ListEntry = NetRoot->VirtualNetRoots.Flink;
3474                                  ListEntry != &NetRoot->VirtualNetRoots;
3475                                  ListEntry = ListEntry->Flink)
3476                             {
3477                                 SavedVNetRoot = CONTAINING_RECORD(ListEntry, V_NET_ROOT, NetRootListEntry);
3478                                 Status = RxCheckVNetRootCredentials(RxContext, SavedVNetRoot,
3479                                                                     &LogonId, UserName,
3480                                                                     UserDomain, Password,
3481                                                                     Flags);
3482                                 if (Status != STATUS_MORE_PROCESSING_REQUIRED)
3483                                 {
3484                                     break;
3485                                 }
3486                             }
3487 
3488                             if (ListEntry == &NetRoot->VirtualNetRoots)
3489                             {
3490                                 SavedVNetRoot = NULL;
3491                             }
3492                         }
3493 
3494                         if (!NT_SUCCESS(Status))
3495                         {
3496                             SavedVNetRoot = NULL;
3497                         }
3498 
3499                         RxUninitializeVNetRootParameters(UserName, UserDomain, Password, &Flags);
3500                     }
3501                 }
3502 
3503                 /* We'll fail, if we had referenced a VNetRoot, dereference it */
3504                 if (Status != STATUS_MORE_PROCESSING_REQUIRED && !NT_SUCCESS(Status))
3505                 {
3506                     if (SavedVNetRoot == NULL)
3507                     {
3508                         RxDereferenceVNetRoot(VNetRoot, LockHoldingState);
3509                     }
3510                 }
3511                 /* Reference VNetRoot we'll keep, and dereference current */
3512                 else if (SavedVNetRoot != VNetRoot)
3513                 {
3514                     RxDereferenceVNetRoot(VNetRoot, LockHoldingState);
3515                     if (SavedVNetRoot != NULL)
3516                     {
3517                         RxReferenceVNetRoot(SavedVNetRoot);
3518                     }
3519                 }
3520             }
3521 
3522             /* We may have found something, or we fail hard, so don't attempt to create a VNetRoot */
3523             if (Status != STATUS_MORE_PROCESSING_REQUIRED)
3524             {
3525                 Construct = FALSE;
3526                 break;
3527             }
3528         }
3529 
3530         /* If we're locked exclusive, we won't loop again, it was the second pass */
3531         if (LockHoldingState != LHS_SharedLockHeld)
3532         {
3533             break;
3534         }
3535 
3536         /* Otherwise, prepare for second pass, exclusive, making sure we can acquire without delay */
3537         if (RxAcquirePrefixTableLockExclusive(RxDeviceObject->pRxNetNameTable, FALSE))
3538         {
3539             RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
3540             LockHoldingState = LHS_ExclusiveLockHeld;
3541             break;
3542         }
3543 
3544         RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
3545         RxAcquirePrefixTableLockExclusive(RxDeviceObject->pRxNetNameTable, TRUE);
3546         LockHoldingState = LHS_ExclusiveLockHeld;
3547     }
3548 
3549     /* We didn't fail, and didn't find any VNetRoot, construct one */
3550     if (Construct)
3551     {
3552         ASSERT(LockHoldingState == LHS_ExclusiveLockHeld);
3553 
3554         Status = RxConstructVirtualNetRoot(RxContext, CanonicalName, NetRootType, &VNetRoot, &LockHoldingState, &ConnectionID);
3555         ASSERT(Status != STATUS_SUCCESS || LockHoldingState != LHS_LockNotHeld);
3556 
3557         if (Status == STATUS_SUCCESS)
3558         {
3559             DPRINT("CanonicalName: %wZ (%d)\n", CanonicalName, CanonicalName->Length);
3560             DPRINT("VNetRoot: %wZ (%d)\n", &VNetRoot->PrefixEntry.Prefix, VNetRoot->PrefixEntry.Prefix.Length);
3561             ASSERT(CanonicalName->Length >= VNetRoot->PrefixEntry.Prefix.Length);
3562 
3563             RemainingName->Buffer = Add2Ptr(CanonicalName->Buffer, VNetRoot->PrefixEntry.Prefix.Length);
3564             RemainingName->Length = CanonicalName->Length - VNetRoot->PrefixEntry.Prefix.Length;
3565             RemainingName->MaximumLength = RemainingName->Length;
3566 
3567             if (BooleanFlagOn(Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE))
3568             {
3569                 DPRINT("CSC instance, VNetRoot: %p\n", VNetRoot);
3570             }
3571             VNetRoot->Flags |= Flags;
3572         }
3573     }
3574 
3575     /* Release the prefix table - caller expects it to be released */
3576     if (LockHoldingState != LHS_LockNotHeld)
3577     {
3578         RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
3579     }
3580 
3581     /* If we failed creating, quit */
3582     if (Status != STATUS_SUCCESS)
3583     {
3584         DPRINT1("RxFindOrConstructVirtualNetRoot() = Status: %x\n", Status);
3585         return Status;
3586     }
3587 
3588     /* Otherwise, wait until the VNetRoot is stable */
3589     DPRINT("Waiting for stable condition for: %p\n", VNetRoot);
3590     RxWaitForStableVNetRoot(VNetRoot, RxContext);
3591     /* It's all good, update the RX_CONTEXT with all our structs */
3592     if (VNetRoot->Condition == Condition_Good)
3593     {
3594         PNET_ROOT NetRoot;
3595 
3596         NetRoot = VNetRoot->NetRoot;
3597         RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
3598         RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
3599         RxContext->Create.pSrvCall = (PMRX_SRV_CALL)NetRoot->SrvCall;
3600     }
3601     else
3602     {
3603         RxDereferenceVNetRoot(VNetRoot, LHS_LockNotHeld);
3604         RxContext->Create.pVNetRoot = NULL;
3605         Status = STATUS_BAD_NETWORK_PATH;
3606     }
3607 
3608     return Status;
3609 }
3610 
3611 /*
3612  * @implemented
3613  */
3614 NTSTATUS
3615 RxFindOrCreateConnections(
3616     _In_ PRX_CONTEXT RxContext,
3617     _In_ PUNICODE_STRING CanonicalName,
3618     _In_ NET_ROOT_TYPE NetRootType,
3619     _Out_ PUNICODE_STRING LocalNetRootName,
3620     _Out_ PUNICODE_STRING FilePathName,
3621     _Inout_ PLOCK_HOLDING_STATE LockState,
3622     _In_ PRX_CONNECTION_ID RxConnectionId)
3623 {
3624     PVOID Container;
3625     PSRV_CALL SrvCall;
3626     PNET_ROOT NetRoot;
3627     PV_NET_ROOT VNetRoot;
3628     NTSTATUS Status = STATUS_UNSUCCESSFUL;
3629     PRX_PREFIX_TABLE PrefixTable;
3630     UNICODE_STRING RemainingName, NetRootName;
3631 
3632     PAGED_CODE();
3633 
3634     DPRINT("RxFindOrCreateConnections(%p, %wZ, %x, %p, %p, %p, %p)\n",
3635            RxContext, CanonicalName, NetRootType, LocalNetRootName,
3636            FilePathName, LockState, RxConnectionId);
3637 
3638     *FilePathName = *CanonicalName;
3639     LocalNetRootName->Length = 0;
3640     LocalNetRootName->MaximumLength = 0;
3641     LocalNetRootName->Buffer = CanonicalName->Buffer;
3642 
3643     /* UNC path, split it */
3644     if (FilePathName->Buffer[1] == ';')
3645     {
3646         BOOLEAN Slash;
3647         USHORT i, Length;
3648 
3649         Slash = FALSE;
3650         for (i = 2; i < FilePathName->Length / sizeof(WCHAR); ++i)
3651         {
3652             if (FilePathName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR)
3653             {
3654                 Slash = TRUE;
3655                 break;
3656             }
3657         }
3658 
3659         if (!Slash)
3660         {
3661             return STATUS_OBJECT_NAME_INVALID;
3662         }
3663 
3664         FilePathName->Buffer = &FilePathName->Buffer[i];
3665         Length = (USHORT)((ULONG_PTR)FilePathName->Buffer - (ULONG_PTR)LocalNetRootName->Buffer);
3666         LocalNetRootName->Length = Length;
3667         LocalNetRootName->MaximumLength = Length;
3668         FilePathName->Length -= Length;
3669 
3670         DPRINT("CanonicalName: %wZ\n", CanonicalName);
3671         DPRINT(" -> FilePathName: %wZ\n", FilePathName);
3672         DPRINT(" -> LocalNetRootName: %wZ\n", LocalNetRootName);
3673     }
3674 
3675     Container = NULL;
3676     PrefixTable = RxContext->RxDeviceObject->pRxNetNameTable;
3677 
3678     _SEH2_TRY
3679     {
3680 RetryLookup:
3681         ASSERT(*LockState != LHS_LockNotHeld);
3682 
3683         /* If previous lookup left something, dereference it */
3684         if (Container != NULL)
3685         {
3686             switch (NodeType(Container))
3687             {
3688                 case RDBSS_NTC_SRVCALL:
3689                     RxDereferenceSrvCall(Container, *LockState);
3690                     break;
3691 
3692                 case RDBSS_NTC_NETROOT:
3693                     RxDereferenceNetRoot(Container, *LockState);
3694                     break;
3695 
3696                 case RDBSS_NTC_V_NETROOT:
3697                     RxDereferenceVNetRoot(Container, *LockState);
3698                     break;
3699 
3700                 default:
3701                     /* Should never happen */
3702                     ASSERT(FALSE);
3703                     break;
3704             }
3705         }
3706 
3707         /* Look for our NetRoot in prefix table */
3708         Container = RxPrefixTableLookupName(PrefixTable, FilePathName, &RemainingName, RxConnectionId);
3709         DPRINT("Container %p for path %wZ\n", Container, FilePathName);
3710 
3711         while (TRUE)
3712         {
3713             UNICODE_STRING SrvCallName;
3714 
3715             SrvCall = NULL;
3716             NetRoot = NULL;
3717             VNetRoot = NULL;
3718 
3719             /* Assume we didn't succeed */
3720             RxContext->Create.pVNetRoot = NULL;
3721             RxContext->Create.pNetRoot = NULL;
3722             RxContext->Create.pSrvCall = NULL;
3723             RxContext->Create.Type = NetRootType;
3724 
3725             /* If we found something */
3726             if (Container != NULL)
3727             {
3728                 /* A VNetRoot */
3729                 if (NodeType(Container) == RDBSS_NTC_V_NETROOT)
3730                 {
3731                     VNetRoot = Container;
3732                     /* Use its NetRoot */
3733                     NetRoot = VNetRoot->NetRoot;
3734 
3735                     /* If it's not stable, wait for it to be stable */
3736                     if (NetRoot->Condition == Condition_InTransition)
3737                     {
3738                         RxReleasePrefixTableLock(PrefixTable);
3739                         DPRINT("Waiting for stable condition for: %p\n", NetRoot);
3740                         RxWaitForStableNetRoot(NetRoot, RxContext);
3741                         RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
3742                         *LockState = LHS_ExclusiveLockHeld;
3743 
3744                         /* Now that's it's ok, retry lookup to find what we want */
3745                         if (NetRoot->Condition == Condition_Good)
3746                         {
3747                             goto RetryLookup;
3748                         }
3749                     }
3750 
3751                     /* Is the associated netroot good? */
3752                     if (NetRoot->Condition == Condition_Good)
3753                     {
3754                         SrvCall = (PSRV_CALL)NetRoot->pSrvCall;
3755 
3756                         /* If it is, and SrvCall as well, then, we have our active connection */
3757                         if (SrvCall->Condition == Condition_Good &&
3758                             SrvCall->RxDeviceObject == RxContext->RxDeviceObject)
3759                         {
3760                             RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
3761                             RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
3762                             RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
3763 
3764                             Status = STATUS_CONNECTION_ACTIVE;
3765                             _SEH2_LEAVE;
3766                         }
3767                     }
3768 
3769                     /* If VNetRoot was well constructed, it means the connection is active */
3770                     if (VNetRoot->ConstructionStatus == STATUS_SUCCESS)
3771                     {
3772                         Status = STATUS_CONNECTION_ACTIVE;
3773                     }
3774                     else
3775                     {
3776                         Status = VNetRoot->ConstructionStatus;
3777                     }
3778 
3779                     RxDereferenceVNetRoot(VNetRoot, *LockState);
3780                     _SEH2_LEAVE;
3781                 }
3782                 /* Can only be a SrvCall */
3783                 else
3784                 {
3785                     ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
3786                     SrvCall = Container;
3787 
3788                     /* Wait for the SRV_CALL to be stable */
3789                     if (SrvCall->Condition == Condition_InTransition)
3790                     {
3791                         RxReleasePrefixTableLock(PrefixTable);
3792                         DPRINT("Waiting for stable condition for: %p\n", SrvCall);
3793                         RxWaitForStableSrvCall(SrvCall, RxContext);
3794                         RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
3795                         *LockState = LHS_ExclusiveLockHeld;
3796 
3797                         /* It went good, loop again to find what we look for */
3798                         if (SrvCall->Condition == Condition_Good)
3799                         {
3800                             goto RetryLookup;
3801                         }
3802                     }
3803 
3804                     /* If it's not good... */
3805                     if (SrvCall->Condition != Condition_Good)
3806                     {
3807                         /* But SRV_CALL was well constructed, assume a connection was active */
3808                         if (SrvCall->Status == STATUS_SUCCESS)
3809                         {
3810                             Status = STATUS_CONNECTION_ACTIVE;
3811                         }
3812                         else
3813                         {
3814                             Status = SrvCall->Status;
3815                         }
3816 
3817                         RxDereferenceSrvCall(SrvCall, *LockState);
3818                         _SEH2_LEAVE;
3819                     }
3820                 }
3821             }
3822 
3823             /* If we found a SRV_CALL not matching our DO, quit */
3824             if (SrvCall != NULL && SrvCall->Condition == Condition_Good &&
3825                 SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
3826             {
3827                 RxDereferenceSrvCall(SrvCall, *LockState);
3828                 Status = STATUS_BAD_NETWORK_NAME;
3829                 _SEH2_LEAVE;
3830             }
3831 
3832             /* Now, we want exclusive lock */
3833             if (*LockState == LHS_SharedLockHeld)
3834             {
3835                 if (!RxAcquirePrefixTableLockExclusive(PrefixTable, FALSE))
3836                 {
3837                     RxReleasePrefixTableLock(PrefixTable);
3838                     RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
3839                     *LockState = LHS_ExclusiveLockHeld;
3840                     goto RetryLookup;
3841                 }
3842 
3843                 RxReleasePrefixTableLock(PrefixTable);
3844                 *LockState = LHS_ExclusiveLockHeld;
3845             }
3846 
3847             ASSERT(*LockState == LHS_ExclusiveLockHeld);
3848 
3849             /* If we reach that point, we found something, no need to create something */
3850             if (Container != NULL)
3851             {
3852                 break;
3853             }
3854 
3855             /* Get the name for the SRV_CALL */
3856             RxExtractServerName(FilePathName, &SrvCallName, NULL);
3857             DPRINT(" -> SrvCallName: %wZ\n", &SrvCallName);
3858             /* And create the SRV_CALL */
3859             SrvCall = RxCreateSrvCall(RxContext, &SrvCallName, NULL, RxConnectionId);
3860             if (SrvCall == NULL)
3861             {
3862                 Status = STATUS_INSUFFICIENT_RESOURCES;
3863                 _SEH2_LEAVE;
3864             }
3865 
3866             /* Reset RX_CONTEXT, so far, connection creation isn't a success */
3867             RxReferenceSrvCall(SrvCall);
3868             RxContext->Create.pVNetRoot = NULL;
3869             RxContext->Create.pNetRoot = NULL;
3870             RxContext->Create.pSrvCall = NULL;
3871             RxContext->Create.Type = NetRootType;
3872             Container = SrvCall;
3873 
3874             /* Construct SRV_CALL, ie, use mini-rdr */
3875             Status = RxConstructSrvCall(RxContext, SrvCall, LockState);
3876             ASSERT(Status != STATUS_SUCCESS || RxIsPrefixTableLockAcquired(PrefixTable));
3877             if (Status != STATUS_SUCCESS)
3878             {
3879                 DPRINT1("RxConstructSrvCall() = Status: %x\n", Status);
3880                 RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
3881                 RxDereferenceSrvCall(SrvCall, *LockState);
3882                 RxReleasePrefixTableLock(PrefixTable);
3883                 _SEH2_LEAVE;
3884             }
3885 
3886             /* Loop again to make use of SRV_CALL stable condition wait */
3887         }
3888 
3889         /* At that point, we have a stable SRV_CALL (either found or constructed) */
3890         ASSERT((NodeType(SrvCall) == RDBSS_NTC_SRVCALL) && (SrvCall->Condition == Condition_Good));
3891         ASSERT(NetRoot == NULL && VNetRoot == NULL);
3892         ASSERT(SrvCall->RxDeviceObject == RxContext->RxDeviceObject);
3893 
3894         /* Call mini-rdr to get NetRoot name */
3895         SrvCall->RxDeviceObject->Dispatch->MRxExtractNetRootName(FilePathName, (PMRX_SRV_CALL)SrvCall, &NetRootName, NULL);
3896         /* And create the NetRoot with that name */
3897         NetRoot = RxCreateNetRoot(SrvCall, &NetRootName, 0, RxConnectionId);
3898         if (NetRoot == NULL)
3899         {
3900             Status = STATUS_INSUFFICIENT_RESOURCES;
3901             _SEH2_LEAVE;
3902         }
3903         NetRoot->Type = NetRootType;
3904 
3905         RxDereferenceSrvCall(SrvCall, *LockState);
3906 
3907         /* Finally, create the associated VNetRoot */
3908         VNetRoot = RxCreateVNetRoot(RxContext, NetRoot, CanonicalName, LocalNetRootName, FilePathName, RxConnectionId);
3909         if (VNetRoot == NULL)
3910         {
3911             RxFinalizeNetRoot(NetRoot, TRUE, TRUE);
3912             Status = STATUS_INSUFFICIENT_RESOURCES;
3913             _SEH2_LEAVE;
3914         }
3915         RxReferenceVNetRoot(VNetRoot);
3916 
3917         /* We're get closer! */
3918         NetRoot->Condition = Condition_InTransition;
3919         RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
3920         RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
3921         RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
3922 
3923         /* Construct the NetRoot, involving the mini-rdr now that we have our three control structs */
3924         Status = RxConstructNetRoot(RxContext, SrvCall, NetRoot, VNetRoot, LockState);
3925         if (!NT_SUCCESS(Status))
3926         {
3927             RxTransitionVNetRoot(VNetRoot, Condition_Bad);
3928             DPRINT1("RxConstructNetRoot failed Ctxt: %p, VNet: %p, Status: %lx, Condition: %d\n", RxContext, VNetRoot, Status, VNetRoot->Condition);
3929             RxDereferenceVNetRoot(VNetRoot, *LockState);
3930 
3931             RxContext->Create.pNetRoot = NULL;
3932             RxContext->Create.pVNetRoot = NULL;
3933         }
3934         else
3935         {
3936             PIO_STACK_LOCATION Stack;
3937 
3938             ASSERT(*LockState == LHS_ExclusiveLockHeld);
3939 
3940             Stack = RxContext->CurrentIrpSp;
3941             if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_CREATE_TREE_CONNECTION))
3942             {
3943                 RxExclusivePrefixTableLockToShared(PrefixTable);
3944                 *LockState = LHS_SharedLockHeld;
3945             }
3946         }
3947     }
3948     _SEH2_FINALLY
3949     {
3950         if (Status != STATUS_SUCCESS && Status != STATUS_CONNECTION_ACTIVE)
3951         {
3952             if (*LockState != LHS_LockNotHeld)
3953             {
3954                 RxReleasePrefixTableLock(PrefixTable);
3955                 *LockState = LHS_LockNotHeld;
3956             }
3957         }
3958     }
3959     _SEH2_END;
3960 
3961     DPRINT("RxFindOrCreateConnections() = Status: %x\n", Status);
3962     return Status;
3963 }
3964 
3965 /*
3966  * @implemented
3967  */
3968 VOID
3969 NTAPI
3970 RxFinishFcbInitialization(
3971     IN OUT PMRX_FCB Fcb,
3972     IN RX_FILE_TYPE FileType,
3973     IN PFCB_INIT_PACKET InitPacket OPTIONAL)
3974 {
3975     RX_FILE_TYPE OldType;
3976 
3977     PAGED_CODE();
3978 
3979     DPRINT("RxFinishFcbInitialization(%p, %x, %p)\n", Fcb, FileType, InitPacket);
3980 
3981     OldType = NodeType(Fcb);
3982     NodeType(Fcb) = FileType;
3983     /* If mini-rdr already did the job for mailslot attributes, 0 the rest */
3984     if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET) && FileType == RDBSS_NTC_MAILSLOT)
3985     {
3986         FILL_IN_FCB((PFCB)Fcb, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3987     }
3988     /* Otherwise, if mini-rdr provided us with an init packet, copy its data */
3989     else if (InitPacket != NULL)
3990     {
3991         FILL_IN_FCB((PFCB)Fcb, *InitPacket->pAttributes, *InitPacket->pNumLinks,
3992                     InitPacket->pCreationTime->QuadPart, InitPacket->pLastAccessTime->QuadPart,
3993                     InitPacket->pLastWriteTime->QuadPart, InitPacket->pLastChangeTime->QuadPart,
3994                     InitPacket->pAllocationSize->QuadPart, InitPacket->pFileSize->QuadPart,
3995                     InitPacket->pValidDataLength->QuadPart);
3996     }
3997 
3998     if (FileType != RDBSS_NTC_STORAGE_TYPE_UNKNOWN &&
3999         FileType != RDBSS_NTC_STORAGE_TYPE_DIRECTORY)
4000     {
4001         /* If our FCB newly points to a file, initiliaze everything related */
4002         if (FileType == RDBSS_NTC_STORAGE_TYPE_FILE)
4003 
4004         {
4005             if (OldType != RDBSS_NTC_STORAGE_TYPE_FILE)
4006             {
4007                 RxInitializeLowIoPerFcbInfo(&((PFCB)Fcb)->Specific.Fcb.LowIoPerFcbInfo);
4008                 FsRtlInitializeFileLock(&((PFCB)Fcb)->Specific.Fcb.FileLock, RxLockOperationCompletion,
4009                                         RxUnlockOperation);
4010 
4011                 ((PFCB)Fcb)->BufferedLocks.List = NULL;
4012                 ((PFCB)Fcb)->BufferedLocks.PendingLockOps = 0;
4013 
4014                 Fcb->Header.IsFastIoPossible = FastIoIsQuestionable;
4015             }
4016         }
4017         /* If not a file, validate type */
4018         else
4019         {
4020             ASSERT(FileType >= RDBSS_NTC_SPOOLFILE && FileType <= RDBSS_NTC_MAILSLOT);
4021         }
4022     }
4023 }
4024 
4025 /*
4026  * @implemented
4027  */
4028 NTSTATUS
4029 RxFinishSrvCallConstruction(
4030     PMRX_SRVCALLDOWN_STRUCTURE Calldown)
4031 {
4032     NTSTATUS Status;
4033     PSRV_CALL SrvCall;
4034     PRX_CONTEXT Context;
4035     RX_BLOCK_CONDITION Condition;
4036     PRX_PREFIX_TABLE PrefixTable;
4037 
4038     DPRINT("RxFinishSrvCallConstruction(%p)\n", Calldown);
4039 
4040     SrvCall = (PSRV_CALL)Calldown->SrvCall;
4041     Context = Calldown->RxContext;
4042     PrefixTable = Context->RxDeviceObject->pRxNetNameTable;
4043 
4044     /* We have a winner, notify him */
4045     if (Calldown->BestFinisher != NULL)
4046     {
4047         DPRINT("Notify the winner: %p (%wZ)\n", Calldown->BestFinisher, &Calldown->BestFinisher->DeviceName);
4048 
4049         ASSERT(SrvCall->RxDeviceObject == Calldown->BestFinisher);
4050 
4051         MINIRDR_CALL_THROUGH(Status, Calldown->BestFinisher->Dispatch,
4052                              MRxSrvCallWinnerNotify,
4053                              ((PMRX_SRV_CALL)SrvCall, TRUE,
4054                               Calldown->CallbackContexts[Calldown->BestFinisherOrdinal].RecommunicateContext));
4055         if (Status != STATUS_SUCCESS)
4056         {
4057             Condition = Condition_Bad;
4058         }
4059         else
4060         {
4061             Condition = Condition_Good;
4062         }
4063     }
4064     /* Otherwise, just fail our SRV_CALL */
4065     else
4066     {
4067         Status = Calldown->CallbackContexts[0].Status;
4068         Condition = Condition_Bad;
4069     }
4070 
4071     RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
4072     RxTransitionSrvCall(SrvCall, Condition);
4073     RxFreePoolWithTag(Calldown, RX_SRVCALL_POOLTAG);
4074 
4075     /* If async, finish it here, otherwise, caller has already finished the stuff */
4076     if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
4077     {
4078         DPRINT("Finishing async call\n");
4079 
4080         RxReleasePrefixTableLock(PrefixTable);
4081 
4082         /* Make sure we weren't cancelled in-between */
4083         if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_CANCELLED))
4084         {
4085             Status = STATUS_CANCELLED;
4086         }
4087 
4088         /* In case that was a create, context can be reused */
4089         if (Context->MajorFunction == IRP_MJ_CREATE)
4090         {
4091             RxpPrepareCreateContextForReuse(Context);
4092         }
4093 
4094         /* If that's a failure, reset everything and return failure */
4095         if (Status != STATUS_SUCCESS)
4096         {
4097             Context->MajorFunction = Context->CurrentIrpSp->MajorFunction;
4098             if (Context->MajorFunction == IRP_MJ_DEVICE_CONTROL)
4099             {
4100                 if (Context->Info.Buffer != NULL)
4101                 {
4102                     RxFreePool(Context->Info.Buffer);
4103                     Context->Info.Buffer = NULL;
4104                 }
4105             }
4106             Context->CurrentIrp->IoStatus.Information = 0;
4107             Context->CurrentIrp->IoStatus.Status = Status;
4108             RxCompleteRequest(Context, Status);
4109         }
4110         /* Otherwise, call resume routine and done! */
4111         else
4112         {
4113             Status = Context->ResumeRoutine(Context);
4114             if (Status != STATUS_PENDING)
4115             {
4116                 RxCompleteRequest(Context, Status);
4117             }
4118 
4119             DPRINT("Not completing, pending\n");
4120         }
4121     }
4122 
4123     RxDereferenceSrvCall(SrvCall, LHS_LockNotHeld);
4124     return Status;
4125 }
4126 
4127 /*
4128  * @implemented
4129  */
4130 VOID
4131 NTAPI
4132 RxFinishSrvCallConstructionDispatcher(
4133     IN PVOID Context)
4134 {
4135     KIRQL OldIrql;
4136     BOOLEAN Direct, KeepLoop;
4137 
4138     DPRINT("RxFinishSrvCallConstructionDispatcher(%p)\n", Context);
4139 
4140     /* In case of failure of starting dispatcher, context is not set
4141      * We keep track of it to fail associated SRV_CALL
4142      */
4143     Direct = (Context == NULL);
4144 
4145     /* Separated thread, loop forever */
4146     while (TRUE)
4147     {
4148         PLIST_ENTRY ListEntry;
4149         PMRX_SRVCALLDOWN_STRUCTURE Calldown;
4150 
4151         /* If there are no SRV_CALL to finalize left, just finish thread */
4152         KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
4153         if (IsListEmpty(&RxSrvCalldownList))
4154         {
4155             KeepLoop =  FALSE;
4156             RxSrvCallConstructionDispatcherActive = FALSE;
4157         }
4158         /* Otherwise, get the SRV_CALL to finish construction */
4159         else
4160         {
4161             ListEntry = RemoveHeadList(&RxSrvCalldownList);
4162             KeepLoop =  TRUE;
4163         }
4164         KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
4165 
4166         /* Nothing to do */
4167         if (!KeepLoop)
4168         {
4169             break;
4170         }
4171 
4172         /* If direct is set, reset the finisher to avoid electing a winner
4173          * and fail SRV_CALL (see upper comment)
4174          */
4175         Calldown = CONTAINING_RECORD(ListEntry, MRX_SRVCALLDOWN_STRUCTURE, SrvCalldownList);
4176         if (Direct)
4177         {
4178             Calldown->BestFinisher = NULL;
4179         }
4180         /* Finish SRV_CALL construction */
4181         RxFinishSrvCallConstruction(Calldown);
4182     }
4183 }
4184 
4185 /*
4186  * @implemented
4187  */
4188 NTSTATUS
4189 RxFlushFcbInSystemCache(
4190     IN PFCB Fcb,
4191     IN BOOLEAN SynchronizeWithLazyWriter)
4192 {
4193     IO_STATUS_BLOCK IoStatus;
4194 
4195     PAGED_CODE();
4196 
4197     /* Deal with Cc */
4198     CcFlushCache(&Fcb->NonPaged->SectionObjectPointers, NULL, 0, &IoStatus);
4199     /* If we're asked to sync with LW, do it in case of success */
4200     if (SynchronizeWithLazyWriter && NT_SUCCESS(IoStatus.Status))
4201     {
4202         RxAcquirePagingIoResource((PRX_CONTEXT)NULL, Fcb);
4203         RxReleasePagingIoResource((PRX_CONTEXT)NULL, Fcb);
4204     }
4205 
4206     DPRINT("Flushing for FCB %p returns %lx\n", Fcb, IoStatus.Status);
4207     return IoStatus.Status;
4208 }
4209 
4210 /*
4211  * @implemented
4212  */
4213 VOID
4214 RxFreeFcbObject(
4215     PVOID Object)
4216 {
4217     PAGED_CODE();
4218 
4219     DPRINT("Freeing %p\n", Object);
4220 
4221     /* If that's a FOBX/SRV_OPEN, nothing to do, just free it */
4222     if (NodeType(Object) == RDBSS_NTC_FOBX || NodeType(Object) == RDBSS_NTC_SRVOPEN)
4223     {
4224         RxFreePoolWithTag(Object, RX_FCB_POOLTAG);
4225     }
4226     /* If that's a FCB... */
4227     else if (NodeTypeIsFcb(Object))
4228     {
4229         PFCB Fcb;
4230         PRDBSS_DEVICE_OBJECT DeviceObject;
4231 
4232         Fcb = (PFCB)Object;
4233         DeviceObject = Fcb->RxDeviceObject;
4234 
4235         /* Delete per stream contexts */
4236         FsRtlTeardownPerStreamContexts(&Fcb->Header);
4237 
4238         SetFlag(Fcb->Header.Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH);
4239 
4240         /* If there was a non-paged FCB allocated, free it */
4241         if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE))
4242         {
4243             RxFreePoolWithTag(Fcb->NonPaged, RX_NONPAGEDFCB_POOLTAG);
4244         }
4245 
4246         /* Free the FCB */
4247         RxFreePool(Fcb);
4248 
4249         /* Update statistics */
4250         InterlockedDecrement(&RxNumberOfActiveFcbs);
4251         InterlockedDecrement((volatile long *)&DeviceObject->NumberOfActiveFcbs);
4252     }
4253 }
4254 
4255 /*
4256  * @implemented
4257  */
4258 VOID
4259 RxFreeObject(
4260     PVOID pObject)
4261 {
4262     PAGED_CODE();
4263 
4264     /* First, perform a few sanity checks if we're dealing with a SRV_CALL or a NET_ROOT */
4265     if (NodeType(pObject) == RDBSS_NTC_SRVCALL)
4266     {
4267         PSRV_CALL SrvCall;
4268         PRDBSS_DEVICE_OBJECT DeviceObject;
4269 
4270         SrvCall = (PSRV_CALL)pObject;
4271         DeviceObject = SrvCall->RxDeviceObject;
4272         if (DeviceObject != NULL)
4273         {
4274             if (!BooleanFlagOn(DeviceObject->Dispatch->MRxFlags, RDBSS_MANAGE_SRV_CALL_EXTENSION))
4275             {
4276                 ASSERT(SrvCall->Context == NULL);
4277             }
4278 
4279             ASSERT(SrvCall->Context2 == NULL);
4280 
4281             SrvCall->RxDeviceObject = NULL;
4282         }
4283     }
4284     else if (NodeType(pObject) == RDBSS_NTC_NETROOT)
4285     {
4286         PNET_ROOT NetRoot;
4287 
4288         NetRoot = (PNET_ROOT)pObject;
4289         NetRoot->pSrvCall = NULL;
4290         NetRoot->NodeTypeCode = NodeType(pObject) | 0xF000;
4291     }
4292 
4293     /* And just free the object */
4294     RxFreePool(pObject);
4295 }
4296 
4297 /*
4298  * @implemented
4299  */
4300 VOID
4301 RxGatherRequestsForSrvOpen(
4302     IN OUT PSRV_CALL SrvCall,
4303     IN PSRV_OPEN SrvOpen,
4304     IN OUT PLIST_ENTRY RequestsListHead)
4305 {
4306     KIRQL OldIrql;
4307     LIST_ENTRY Discarded, *Entry;
4308     PCHANGE_BUFFERING_STATE_REQUEST Request;
4309 
4310     /* Dispatch any pending operation first */
4311     RxpDispatchChangeBufferingStateRequests(SrvCall, SrvOpen, &Discarded);
4312 
4313     /* Then, get any entry related to our key and SRV_OPEN */
4314     KeAcquireSpinLock(&SrvCall->BufferingManager.SpinLock, &OldIrql);
4315     Entry = SrvCall->BufferingManager.HandlerList.Flink;
4316     while (Entry != &SrvCall->BufferingManager.HandlerList)
4317     {
4318         Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
4319         Entry = Entry->Flink;
4320         if (Request->SrvOpenKey == SrvOpen->Key && Request->SrvOpen == SrvOpen)
4321         {
4322             RemoveEntryList(&Request->ListEntry);
4323             InsertTailList(RequestsListHead, &Request->ListEntry);
4324         }
4325     }
4326     KeReleaseSpinLock(&SrvCall->BufferingManager.SpinLock, OldIrql);
4327 
4328     /* Perform the same search in the last change list */
4329     Entry = SrvCall->BufferingManager.LastChanceHandlerList.Flink;
4330     while (Entry != &SrvCall->BufferingManager.LastChanceHandlerList)
4331     {
4332         Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
4333         Entry = Entry->Flink;
4334         if (Request->SrvOpenKey == SrvOpen->Key && Request->SrvOpen == SrvOpen)
4335         {
4336             RemoveEntryList(&Request->ListEntry);
4337             InsertTailList(RequestsListHead, &Request->ListEntry);
4338         }
4339     }
4340 
4341     /* Discard the discarded requests */
4342     RxpDiscardChangeBufferingStateRequests(&Discarded);
4343 }
4344 
4345 /*
4346  * @implemented
4347  */
4348 PRDBSS_DEVICE_OBJECT
4349 RxGetDeviceObjectOfInstance(
4350     PVOID Instance)
4351 {
4352     NODE_TYPE_CODE NodeType;
4353     PRDBSS_DEVICE_OBJECT DeviceObject;
4354 
4355     PAGED_CODE();
4356 
4357     /* We only handle a few object types */
4358     NodeType = NodeType(Instance);
4359     ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
4360            (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) || (NodeType == RDBSS_NTC_FOBX));
4361 
4362     /* Get the device object depending on the object */
4363     switch (NodeType)
4364     {
4365         case RDBSS_NTC_FOBX:
4366         {
4367             PFOBX Fobx;
4368 
4369             Fobx = (PFOBX)Instance;
4370             DeviceObject = Fobx->RxDeviceObject;
4371             break;
4372         }
4373 
4374         case RDBSS_NTC_SRVCALL:
4375         {
4376             PSRV_CALL SrvCall;
4377 
4378             SrvCall = (PSRV_CALL)Instance;
4379             DeviceObject = SrvCall->RxDeviceObject;
4380             break;
4381         }
4382 
4383         case RDBSS_NTC_NETROOT:
4384         {
4385             PNET_ROOT NetRoot;
4386 
4387             NetRoot = (PNET_ROOT)Instance;
4388             DeviceObject = NetRoot->pSrvCall->RxDeviceObject;
4389             break;
4390         }
4391 
4392         case RDBSS_NTC_V_NETROOT:
4393         {
4394             PV_NET_ROOT VNetRoot;
4395 
4396             VNetRoot = (PV_NET_ROOT)Instance;
4397             DeviceObject = VNetRoot->pNetRoot->pSrvCall->RxDeviceObject;
4398             break;
4399         }
4400 
4401         case RDBSS_NTC_SRVOPEN:
4402         {
4403             PSRV_OPEN SrvOpen;
4404 
4405             SrvOpen = (PSRV_OPEN)Instance;
4406             DeviceObject = ((PFCB)SrvOpen->pFcb)->RxDeviceObject;
4407             break;
4408         }
4409 
4410         default:
4411             DeviceObject = NULL;
4412             break;
4413     }
4414 
4415     /* Job done */
4416     return DeviceObject;
4417 }
4418 
4419 /*
4420  * @implemented
4421  */
4422 VOID
4423 RxGetFileSizeWithLock(
4424     IN PFCB Fcb,
4425     OUT PLONGLONG FileSize)
4426 {
4427     PAGED_CODE();
4428 
4429     *FileSize = Fcb->Header.FileSize.QuadPart;
4430 }
4431 
4432 /*
4433  * @implemented
4434  */
4435 PEPROCESS
4436 NTAPI
4437 RxGetRDBSSProcess(
4438     VOID)
4439 {
4440     return RxData.OurProcess;
4441 }
4442 
4443 /*
4444  * @implemented
4445  */
4446 NTSTATUS
4447 RxInitializeBufferingManager(
4448    PSRV_CALL SrvCall)
4449 {
4450     KeInitializeSpinLock(&SrvCall->BufferingManager.SpinLock);
4451     InitializeListHead(&SrvCall->BufferingManager.DispatcherList);
4452     InitializeListHead(&SrvCall->BufferingManager.HandlerList);
4453     InitializeListHead(&SrvCall->BufferingManager.LastChanceHandlerList);
4454     SrvCall->BufferingManager.DispatcherActive = FALSE;
4455     SrvCall->BufferingManager.HandlerInactive = FALSE;
4456     SrvCall->BufferingManager.LastChanceHandlerActive = FALSE;
4457     SrvCall->BufferingManager.NumberOfOutstandingOpens = 0;
4458     InitializeListHead(&SrvCall->BufferingManager.SrvOpenLists[0]);
4459     ExInitializeFastMutex(&SrvCall->BufferingManager.Mutex);
4460 
4461     return STATUS_SUCCESS;
4462 }
4463 
4464 /*
4465  * @implemented
4466  */
4467 VOID
4468 NTAPI
4469 RxInitializeContext(
4470     IN PIRP Irp,
4471     IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
4472     IN ULONG InitialContextFlags,
4473     IN OUT PRX_CONTEXT RxContext)
4474 {
4475     PIO_STACK_LOCATION Stack;
4476 
4477     /* Initialize our various fields */
4478     RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT;
4479     RxContext->NodeByteSize = sizeof(RX_CONTEXT);
4480     RxContext->ReferenceCount = 1;
4481     RxContext->SerialNumber = InterlockedExchangeAdd((volatile LONG *)&RxContextSerialNumberCounter, 1);
4482     RxContext->RxDeviceObject = RxDeviceObject;
4483     KeInitializeEvent(&RxContext->SyncEvent, SynchronizationEvent, FALSE);
4484     RxInitializeScavengerEntry(&RxContext->ScavengerEntry);
4485     InitializeListHead(&RxContext->BlockedOperations);
4486     RxContext->MRxCancelRoutine = NULL;
4487     RxContext->ResumeRoutine = NULL;
4488     RxContext->Flags |= InitialContextFlags;
4489     RxContext->CurrentIrp = Irp;
4490     RxContext->LastExecutionThread = PsGetCurrentThread();
4491     RxContext->OriginalThread = RxContext->LastExecutionThread;
4492 
4493     /* If've got no IRP, mark RX_CONTEXT */
4494     if (Irp == NULL)
4495     {
4496         RxContext->CurrentIrpSp = NULL;
4497         RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1;
4498         RxContext->MinorFunction = 0;
4499     }
4500     else
4501     {
4502         /* Otherwise, first determine whether we are performing async operation */
4503         Stack = IoGetCurrentIrpStackLocation(Irp);
4504         if (Stack->FileObject != NULL)
4505         {
4506             PFCB Fcb;
4507 
4508             Fcb = Stack->FileObject->FsContext;
4509             if (!IoIsOperationSynchronous(Irp) ||
4510                 ((Fcb != NULL && NodeTypeIsFcb(Fcb)) &&
4511                  (Stack->MajorFunction == IRP_MJ_READ || Stack->MajorFunction == IRP_MJ_WRITE || Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
4512                  (Fcb->pNetRoot != NULL && (Fcb->pNetRoot->Type == NET_ROOT_PIPE))))
4513             {
4514                 RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
4515             }
4516         }
4517 
4518         if (Stack->MajorFunction == IRP_MJ_DIRECTORY_CONTROL && Stack->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
4519         {
4520             RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
4521         }
4522         if (Stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
4523         {
4524             RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
4525         }
4526 
4527         /* Set proper flags if TopLevl IRP/Device */
4528         if (!RxIsThisTheTopLevelIrp(Irp))
4529         {
4530             RxContext->Flags |= RX_CONTEXT_FLAG_RECURSIVE_CALL;
4531         }
4532         if (RxGetTopDeviceObjectIfRdbssIrp() == RxDeviceObject)
4533         {
4534             RxContext->Flags |= RX_CONTEXT_FLAG_THIS_DEVICE_TOP_LEVEL;
4535         }
4536 
4537         /* Copy stack information */
4538         RxContext->MajorFunction = Stack->MajorFunction;
4539         RxContext->MinorFunction = Stack->MinorFunction;
4540         ASSERT(RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION);
4541         RxContext->CurrentIrpSp = Stack;
4542 
4543         /* If we have a FO associated, learn for more */
4544         if (Stack->FileObject != NULL)
4545         {
4546             PFCB Fcb;
4547             PFOBX Fobx;
4548 
4549             /* Get the FCB and CCB (FOBX) */
4550             Fcb = Stack->FileObject->FsContext;
4551             Fobx = Stack->FileObject->FsContext2;
4552             RxContext->pFcb = (PMRX_FCB)Fcb;
4553             if (Fcb != NULL && NodeTypeIsFcb(Fcb))
4554             {
4555                 RxContext->NonPagedFcb = Fcb->NonPaged;
4556             }
4557 
4558             /* We have a FOBX, this not a DFS opening, keep track of it */
4559             if (Fobx != NULL && Fobx != UIntToPtr(DFS_OPEN_CONTEXT) && Fobx != UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT))
4560             {
4561                 RxContext->pFobx = (PMRX_FOBX)Fobx;
4562                 RxContext->pRelevantSrvOpen = Fobx->pSrvOpen;
4563                 if (Fobx->NodeTypeCode == RDBSS_NTC_FOBX)
4564                 {
4565                     RxContext->FobxSerialNumber = InterlockedIncrement((volatile LONG *)&Fobx->FobxSerialNumber);
4566                 }
4567             }
4568             else
4569             {
4570                 RxContext->pFobx = NULL;
4571             }
4572 
4573             /* In case of directory change notification, Fobx may be a VNetRoot, take note of that */
4574             if (RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL && RxContext->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY &&
4575                 Fobx != NULL)
4576             {
4577                 PV_NET_ROOT VNetRoot = NULL;
4578 
4579                 if (Fobx->NodeTypeCode == RDBSS_NTC_FOBX)
4580                 {
4581                     VNetRoot = Fcb->VNetRoot;
4582                 }
4583                 else if (Fobx->NodeTypeCode == RDBSS_NTC_V_NETROOT)
4584                 {
4585                     VNetRoot = (PV_NET_ROOT)Fobx;
4586                 }
4587 
4588                 if (VNetRoot != NULL)
4589                 {
4590                     RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
4591                 }
4592             }
4593 
4594             /* Remember if that's a write through file */
4595             RxContext->RealDevice = Stack->FileObject->DeviceObject;
4596             if (BooleanFlagOn(Stack->FileObject->Flags, FO_WRITE_THROUGH))
4597             {
4598                 RxContext->Flags |= RX_CONTEXT_FLAG_WRITE_THROUGH;
4599             }
4600         }
4601     }
4602 
4603     if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL)
4604     {
4605         DPRINT("New Ctxt: %p for MN: %d, IRP: %p, THRD: %p, FCB: %p, FOBX:%p #%lx\n",
4606                RxContext, RxContext->MinorFunction, Irp,
4607                PsGetCurrentThread(), RxContext->pFcb, RxContext->pFobx,
4608                RxContext->SerialNumber);
4609     }
4610 }
4611 
4612 /*
4613  * @implemented
4614  */
4615 VOID
4616 NTAPI
4617 RxInitializeDebugSupport(
4618     VOID)
4619 {
4620     /* Nothing to do */
4621 }
4622 
4623 /*
4624  * @implemented
4625  */
4626 NTSTATUS
4627 NTAPI
4628 RxInitializeDispatcher(
4629     VOID)
4630 {
4631     NTSTATUS Status;
4632     HANDLE ThreadHandle;
4633 
4634     PAGED_CODE();
4635 
4636     RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
4637     RxFileSystemDeviceObject->DispatcherContext.pTearDownEvent = NULL;
4638 
4639     /* Set appropriate timeouts: 10s & 60s */
4640     RxWorkQueueWaitInterval[CriticalWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
4641     RxWorkQueueWaitInterval[DelayedWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
4642     RxWorkQueueWaitInterval[HyperCriticalWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
4643     RxSpinUpDispatcherWaitInterval.QuadPart = -60 * 1000 * 1000 * 10;
4644 
4645     RxDispatcher.NumberOfProcessors = 1;
4646     RxDispatcher.OwnerProcess = IoGetCurrentProcess();
4647     RxDispatcher.pWorkQueueDispatcher = &RxDispatcherWorkQueues;
4648 
4649     /* Initialize our dispatchers */
4650     Status = RxInitializeWorkQueueDispatcher(RxDispatcher.pWorkQueueDispatcher);
4651     if (!NT_SUCCESS(Status))
4652     {
4653         return Status;
4654     }
4655 
4656     Status = RxInitializeMRxDispatcher(RxFileSystemDeviceObject);
4657     if (!NT_SUCCESS(Status))
4658     {
4659         return Status;
4660     }
4661 
4662     /* And start them */
4663     RxDispatcher.State = RxDispatcherActive;
4664     InitializeListHead(&RxDispatcher.SpinUpRequests);
4665     KeInitializeSpinLock(&RxDispatcher.SpinUpRequestsLock);
4666     KeInitializeEvent(&RxDispatcher.SpinUpRequestsEvent, 0, 0);
4667     KeInitializeEvent(&RxDispatcher.SpinUpRequestsTearDownEvent, 0, 0);
4668     Status = PsCreateSystemThread(&ThreadHandle, PROCESS_ALL_ACCESS, NULL,
4669                                   NULL, NULL, RxSpinUpRequestsDispatcher, &RxDispatcher);
4670     if (NT_SUCCESS(Status))
4671     {
4672         ZwClose(ThreadHandle);
4673     }
4674 
4675     return Status;
4676 }
4677 
4678 /*
4679  * @implemented
4680  */
4681 VOID
4682 RxInitializeFcbTable(
4683     IN OUT PRX_FCB_TABLE FcbTable,
4684     IN BOOLEAN CaseInsensitiveMatch)
4685 {
4686     USHORT i;
4687 
4688     PAGED_CODE();
4689 
4690     FcbTable->NodeTypeCode = RDBSS_NTC_FCB_TABLE;
4691     FcbTable->NodeByteSize = sizeof(RX_FCB_TABLE);
4692 
4693     ExInitializeResourceLite(&FcbTable->TableLock);
4694     FcbTable->CaseInsensitiveMatch = CaseInsensitiveMatch;
4695     FcbTable->Version = 0;
4696     FcbTable->TableEntryForNull = NULL;
4697 
4698     FcbTable->NumberOfBuckets = RX_FCB_TABLE_NUMBER_OF_HASH_BUCKETS;
4699     for (i = 0; i < FcbTable->NumberOfBuckets; ++i)
4700     {
4701         InitializeListHead(&FcbTable->HashBuckets[i]);
4702     }
4703 
4704     FcbTable->Lookups = 0;
4705     FcbTable->FailedLookups = 0;
4706     FcbTable->Compares = 0;
4707 }
4708 
4709 /*
4710  * @implemented
4711  */
4712 VOID
4713 NTAPI
4714 RxInitializeLowIoContext(
4715     OUT PLOWIO_CONTEXT LowIoContext,
4716     IN ULONG Operation)
4717 {
4718     PRX_CONTEXT RxContext;
4719     PIO_STACK_LOCATION Stack;
4720 
4721     PAGED_CODE();
4722 
4723     RxContext = CONTAINING_RECORD(LowIoContext, RX_CONTEXT, LowIoContext);
4724     ASSERT(LowIoContext == &RxContext->LowIoContext);
4725 
4726     Stack = RxContext->CurrentIrpSp;
4727 
4728     KeInitializeEvent(&RxContext->SyncEvent, NotificationEvent, FALSE);
4729     RxContext->LowIoContext.ResourceThreadId = (ERESOURCE_THREAD)PsGetCurrentThread();
4730     RxContext->LowIoContext.Operation = Operation;
4731 
4732     switch (Operation)
4733     {
4734         case LOWIO_OP_READ:
4735         case LOWIO_OP_WRITE:
4736             /* In case of RW, set a canary, to make sure these fields are properly set
4737              * they will be asserted when lowio request will be submit to mini-rdr
4738              * See LowIoSubmit()
4739              */
4740             RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset = 0xFFFFFFEE;
4741             RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount = 0xEEEEEEEE;
4742             RxContext->LowIoContext.ParamsFor.ReadWrite.Key = Stack->Parameters.Read.Key;
4743 
4744             /* Keep track of paging IOs */
4745             if (BooleanFlagOn(RxContext->CurrentIrp->Flags, IRP_PAGING_IO))
4746             {
4747                 RxContext->LowIoContext.ParamsFor.ReadWrite.Flags = LOWIO_READWRITEFLAG_PAGING_IO;
4748             }
4749             else
4750             {
4751                 RxContext->LowIoContext.ParamsFor.ReadWrite.Flags = 0;
4752             }
4753 
4754             break;
4755 
4756         case LOWIO_OP_FSCTL:
4757         case LOWIO_OP_IOCTL:
4758             /* This will be initialized later on with a call to RxLowIoPopulateFsctlInfo() */
4759             RxContext->LowIoContext.ParamsFor.FsCtl.Flags = 0;
4760             RxContext->LowIoContext.ParamsFor.FsCtl.InputBufferLength = 0;
4761             RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = NULL;
4762             RxContext->LowIoContext.ParamsFor.FsCtl.OutputBufferLength = 0;
4763             RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = NULL;
4764             RxContext->LowIoContext.ParamsFor.FsCtl.MinorFunction = 0;
4765             break;
4766 
4767         /* Nothing to do for these */
4768         case LOWIO_OP_SHAREDLOCK:
4769         case LOWIO_OP_EXCLUSIVELOCK:
4770         case LOWIO_OP_UNLOCK:
4771         case LOWIO_OP_UNLOCK_MULTIPLE:
4772         case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
4773         case LOWIO_OP_CLEAROUT:
4774             break;
4775 
4776         default:
4777             /* Should never happen */
4778             ASSERT(FALSE);
4779             break;
4780     }
4781 }
4782 
4783 /*
4784  * @implemented
4785  */
4786 VOID
4787 RxInitializeLowIoPerFcbInfo(
4788     PLOWIO_PER_FCB_INFO LowIoPerFcbInfo)
4789 {
4790     PAGED_CODE();
4791 
4792     InitializeListHead(&LowIoPerFcbInfo->PagingIoReadsOutstanding);
4793     InitializeListHead(&LowIoPerFcbInfo->PagingIoWritesOutstanding);
4794 }
4795 
4796 /*
4797  * @implemented
4798  */
4799 NTSTATUS
4800 RxInitializeMRxDispatcher(
4801      IN OUT PRDBSS_DEVICE_OBJECT pMRxDeviceObject)
4802 {
4803     PAGED_CODE();
4804 
4805     pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
4806     pMRxDeviceObject->DispatcherContext.pTearDownEvent = NULL;
4807 
4808     return STATUS_SUCCESS;
4809 }
4810 
4811 /*
4812  * @implemented
4813  */
4814 VOID
4815 RxInitializePrefixTable(
4816     IN OUT PRX_PREFIX_TABLE ThisTable,
4817     IN ULONG TableSize OPTIONAL,
4818     IN BOOLEAN CaseInsensitiveMatch)
4819 {
4820     PAGED_CODE();
4821 
4822     if (TableSize == 0)
4823     {
4824         TableSize = RX_PREFIX_TABLE_DEFAULT_LENGTH;
4825     }
4826 
4827     ThisTable->NodeTypeCode = RDBSS_NTC_PREFIX_TABLE;
4828     ThisTable->NodeByteSize = sizeof(RX_PREFIX_TABLE);
4829     InitializeListHead(&ThisTable->MemberQueue);
4830     ThisTable->Version = 0;
4831     ThisTable->TableEntryForNull = NULL;
4832     ThisTable->IsNetNameTable = FALSE;
4833     ThisTable->CaseInsensitiveMatch = CaseInsensitiveMatch;
4834     ThisTable->TableSize = TableSize;
4835 
4836     if (TableSize > 0)
4837     {
4838         USHORT i;
4839 
4840         for (i = 0; i < RX_PREFIX_TABLE_DEFAULT_LENGTH; ++i)
4841         {
4842             InitializeListHead(&ThisTable->HashBuckets[i]);
4843         }
4844     }
4845 }
4846 
4847 /*
4848  * @implemented
4849  */
4850 VOID
4851 RxInitializePurgeSyncronizationContext(
4852     PPURGE_SYNCHRONIZATION_CONTEXT PurgeSyncronizationContext)
4853 {
4854     PAGED_CODE();
4855 
4856     InitializeListHead(&PurgeSyncronizationContext->ContextsAwaitingPurgeCompletion);
4857     PurgeSyncronizationContext->PurgeInProgress = FALSE;
4858 }
4859 
4860 NTSTATUS
4861 RxInitializeSrvCallParameters(
4862     IN PRX_CONTEXT RxContext,
4863     IN OUT PSRV_CALL SrvCall)
4864 {
4865     PAGED_CODE();
4866 
4867     SrvCall->pPrincipalName = NULL;
4868 
4869     /* We only have stuff to initialize for file opening from DFS */
4870     if (RxContext->MajorFunction != IRP_MJ_CREATE || RxContext->Create.EaLength == 0)
4871     {
4872         return STATUS_SUCCESS;
4873     }
4874 
4875     ASSERT(RxContext->Create.EaBuffer != NULL);
4876 
4877     UNIMPLEMENTED;
4878     return STATUS_NOT_IMPLEMENTED;
4879 }
4880 
4881 /*
4882  * @implemented
4883  */
4884 NTSTATUS
4885 NTAPI
4886 RxInitializeRxTimer(
4887     VOID)
4888 {
4889     PAGED_CODE();
4890 
4891     RxTimerInterval.QuadPart = -550000;
4892     KeInitializeSpinLock(&RxTimerLock);
4893     InitializeListHead(&RxTimerQueueHead);
4894     InitializeListHead(&RxRecurrentWorkItemsList);
4895     KeInitializeDpc(&RxTimerDpc, RxTimerDispatch, NULL);
4896     KeInitializeTimer(&RxTimer);
4897     RxTimerTickCount = 0;
4898 
4899     return STATUS_SUCCESS;
4900 }
4901 
4902 NTSTATUS
4903 RxInitializeVNetRootParameters(
4904    PRX_CONTEXT RxContext,
4905    OUT LUID *LogonId,
4906    OUT PULONG SessionId,
4907    OUT PUNICODE_STRING *UserNamePtr,
4908    OUT PUNICODE_STRING *UserDomainNamePtr,
4909    OUT PUNICODE_STRING *PasswordPtr,
4910    OUT PULONG Flags)
4911 {
4912     NTSTATUS Status;
4913     PACCESS_TOKEN Token;
4914 
4915     PAGED_CODE();
4916 
4917     DPRINT("RxInitializeVNetRootParameters(%p, %p, %p, %p, %p, %p, %p)\n", RxContext,
4918            LogonId, SessionId, UserNamePtr, UserDomainNamePtr, PasswordPtr, Flags);
4919 
4920     *UserNamePtr = NULL;
4921     *UserDomainNamePtr = NULL;
4922     *PasswordPtr = NULL;
4923     /* By default, that's not CSC instance */
4924     *Flags &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE;
4925 
4926     Token = SeQuerySubjectContextToken(&RxContext->Create.NtCreateParameters.SecurityContext->AccessState->SubjectSecurityContext);
4927     if (SeTokenIsRestricted(Token))
4928     {
4929         return STATUS_ACCESS_DENIED;
4930     }
4931 
4932     /* Get LogonId */
4933     Status = SeQueryAuthenticationIdToken(Token, LogonId);
4934     if (!NT_SUCCESS(Status))
4935     {
4936         return Status;
4937     }
4938 
4939     /* And SessionId */
4940     Status = SeQuerySessionIdToken(Token, SessionId);
4941     if (!NT_SUCCESS(Status))
4942     {
4943         return Status;
4944     }
4945 
4946     if (RxContext->Create.UserName.Buffer != NULL)
4947     {
4948         UNIMPLEMENTED;
4949         Status = STATUS_NOT_IMPLEMENTED;
4950         goto Leave;
4951     }
4952 
4953     /* Deal with connection credentials */
4954     if (RxContext->Create.UserDomainName.Buffer != NULL)
4955     {
4956         UNIMPLEMENTED;
4957         Status = STATUS_NOT_IMPLEMENTED;
4958         goto Leave;
4959     }
4960 
4961     if (RxContext->Create.Password.Buffer != NULL)
4962     {
4963         UNIMPLEMENTED;
4964         Status = STATUS_NOT_IMPLEMENTED;
4965         goto Leave;
4966     }
4967 
4968 Leave:
4969     if (NT_SUCCESS(Status))
4970     {
4971         /* If that's a CSC instance, mark it as such */
4972         if (RxIsThisACscAgentOpen(RxContext))
4973         {
4974             *Flags |= VNETROOT_FLAG_CSCAGENT_INSTANCE;
4975         }
4976         return Status;
4977     }
4978 
4979     return Status;
4980 }
4981 
4982 /*
4983  * @implemented
4984  */
4985 VOID
4986 RxInitializeWorkQueue(
4987    PRX_WORK_QUEUE WorkQueue,
4988    WORK_QUEUE_TYPE WorkQueueType,
4989    ULONG MaximumNumberOfWorkerThreads,
4990    ULONG MinimumNumberOfWorkerThreads)
4991 {
4992     PAGED_CODE();
4993 
4994     WorkQueue->Type = WorkQueueType;
4995     WorkQueue->MaximumNumberOfWorkerThreads = MaximumNumberOfWorkerThreads;
4996     WorkQueue->MinimumNumberOfWorkerThreads = MinimumNumberOfWorkerThreads;
4997 
4998     WorkQueue->State = RxWorkQueueActive;
4999     WorkQueue->SpinUpRequestPending = FALSE;
5000     WorkQueue->pRundownContext = NULL;
5001     WorkQueue->NumberOfWorkItemsDispatched = 0;
5002     WorkQueue->NumberOfWorkItemsToBeDispatched = 0;
5003     WorkQueue->CumulativeQueueLength = 0;
5004     WorkQueue->NumberOfSpinUpRequests = 0;
5005     WorkQueue->NumberOfActiveWorkerThreads = 0;
5006     WorkQueue->NumberOfIdleWorkerThreads = 0;
5007     WorkQueue->NumberOfFailedSpinUpRequests = 0;
5008     WorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse = 0;
5009     WorkQueue->WorkQueueItemForTearDownWorkQueue.List.Flink = NULL;
5010     WorkQueue->WorkQueueItemForTearDownWorkQueue.WorkerRoutine = NULL;
5011     WorkQueue->WorkQueueItemForTearDownWorkQueue.Parameter = NULL;
5012     WorkQueue->WorkQueueItemForTearDownWorkQueue.pDeviceObject = NULL;
5013     WorkQueue->WorkQueueItemForSpinUpWorkerThread.List.Flink = NULL;
5014     WorkQueue->WorkQueueItemForSpinUpWorkerThread.WorkerRoutine = NULL;
5015     WorkQueue->WorkQueueItemForSpinUpWorkerThread.Parameter = NULL;
5016     WorkQueue->WorkQueueItemForSpinUpWorkerThread.pDeviceObject = NULL;
5017     WorkQueue->WorkQueueItemForSpinDownWorkerThread.List.Flink = NULL;
5018     WorkQueue->WorkQueueItemForSpinDownWorkerThread.WorkerRoutine = NULL;
5019     WorkQueue->WorkQueueItemForSpinDownWorkerThread.Parameter = NULL;
5020     WorkQueue->WorkQueueItemForSpinDownWorkerThread.pDeviceObject = NULL;
5021 
5022     KeInitializeQueue(&WorkQueue->Queue, MaximumNumberOfWorkerThreads);
5023     KeInitializeSpinLock(&WorkQueue->SpinLock);
5024 }
5025 
5026 /*
5027  * @implemented
5028  */
5029 NTSTATUS
5030 RxInitializeWorkQueueDispatcher(
5031    PRX_WORK_QUEUE_DISPATCHER Dispatcher)
5032 {
5033     NTSTATUS Status;
5034     ULONG MaximumNumberOfWorkerThreads;
5035 
5036     PAGED_CODE();
5037 
5038     /* Number of threads will depend on system capacity */
5039     if (MmQuerySystemSize() != MmLargeSystem)
5040     {
5041         MaximumNumberOfWorkerThreads = 5;
5042     }
5043     else
5044     {
5045         MaximumNumberOfWorkerThreads = 10;
5046     }
5047 
5048     /* Initialize the work queues */
5049     RxInitializeWorkQueue(&Dispatcher->WorkQueue[CriticalWorkQueue], CriticalWorkQueue,
5050                           MaximumNumberOfWorkerThreads, 1);
5051     RxInitializeWorkQueue(&Dispatcher->WorkQueue[DelayedWorkQueue], DelayedWorkQueue, 2, 1);
5052     RxInitializeWorkQueue(&Dispatcher->WorkQueue[HyperCriticalWorkQueue], HyperCriticalWorkQueue, 5, 1);
5053 
5054     /* And start the worker threads */
5055     Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[HyperCriticalWorkQueue],
5056                                   RxBootstrapWorkerThreadDispatcher,
5057                                   &Dispatcher->WorkQueue[HyperCriticalWorkQueue]);
5058     if (!NT_SUCCESS(Status))
5059     {
5060         return Status;
5061     }
5062 
5063     Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[CriticalWorkQueue],
5064                                   RxBootstrapWorkerThreadDispatcher,
5065                                   &Dispatcher->WorkQueue[CriticalWorkQueue]);
5066     if (!NT_SUCCESS(Status))
5067     {
5068         return Status;
5069     }
5070 
5071     Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[DelayedWorkQueue],
5072                                   RxBootstrapWorkerThreadDispatcher,
5073                                   &Dispatcher->WorkQueue[DelayedWorkQueue]);
5074     return Status;
5075 }
5076 
5077 /*
5078  * @implemented
5079  */
5080 VOID
5081 RxInitiateSrvOpenKeyAssociation(
5082    IN OUT PSRV_OPEN SrvOpen)
5083 {
5084     PRX_BUFFERING_MANAGER BufferingManager;
5085 
5086     PAGED_CODE();
5087 
5088     SrvOpen->Key = NULL;
5089 
5090     /* Just keep track of the opening request */
5091     BufferingManager = &((PSRV_CALL)((PFCB)SrvOpen->pFcb)->VNetRoot->pNetRoot->pSrvCall)->BufferingManager;
5092     InterlockedIncrement(&BufferingManager->NumberOfOutstandingOpens);
5093 
5094     InitializeListHead(&SrvOpen->SrvOpenKeyList);
5095 }
5096 
5097 /*
5098  * @implemented
5099  */
5100 NTSTATUS
5101 RxInsertWorkQueueItem(
5102     PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
5103     WORK_QUEUE_TYPE WorkQueueType,
5104     PRX_WORK_QUEUE_ITEM WorkQueueItem)
5105 {
5106     KIRQL OldIrql;
5107     NTSTATUS Status;
5108     BOOLEAN SpinUpThreads;
5109     PRX_WORK_QUEUE WorkQueue;
5110 
5111     /* No dispatcher, nothing to insert */
5112     if (RxDispatcher.State != RxDispatcherActive)
5113     {
5114         return STATUS_UNSUCCESSFUL;
5115     }
5116 
5117     /* Get the work queue */
5118     WorkQueue = &RxDispatcher.pWorkQueueDispatcher->WorkQueue[WorkQueueType];
5119 
5120     KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
5121     /* Only insert if the work queue is in decent state */
5122     if (WorkQueue->State != RxWorkQueueActive || pMRxDeviceObject->DispatcherContext.pTearDownEvent != NULL)
5123     {
5124         Status = STATUS_UNSUCCESSFUL;
5125     }
5126     else
5127     {
5128         SpinUpThreads = FALSE;
5129         WorkQueueItem->pDeviceObject = pMRxDeviceObject;
5130         InterlockedIncrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
5131         WorkQueue->CumulativeQueueLength += WorkQueue->NumberOfWorkItemsToBeDispatched;
5132         InterlockedIncrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
5133 
5134         /* If required (and possible!), spin up a new worker thread */
5135         if (WorkQueue->NumberOfIdleWorkerThreads < WorkQueue->NumberOfWorkItemsToBeDispatched &&
5136             WorkQueue->NumberOfActiveWorkerThreads < WorkQueue->MaximumNumberOfWorkerThreads &&
5137             !WorkQueue->SpinUpRequestPending)
5138         {
5139             WorkQueue->SpinUpRequestPending = TRUE;
5140             SpinUpThreads = TRUE;
5141         }
5142 
5143         Status = STATUS_SUCCESS;
5144     }
5145     KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
5146 
5147     /* If we failed, return and still not insert item */
5148     if (!NT_SUCCESS(Status))
5149     {
5150         return Status;
5151     }
5152 
5153     /* All fine, insert the item */
5154     KeInsertQueue(&WorkQueue->Queue, &WorkQueueItem->List);
5155 
5156     /* And start a new worker thread if needed */
5157     if (SpinUpThreads)
5158     {
5159         RxSpinUpWorkerThreads(WorkQueue);
5160     }
5161 
5162     return Status;
5163 }
5164 
5165 BOOLEAN
5166 RxIsThisACscAgentOpen(
5167     IN PRX_CONTEXT RxContext)
5168 {
5169     BOOLEAN CscAgent;
5170 
5171     CscAgent = FALSE;
5172 
5173     /* Client Side Caching is DFS stuff - we don't support it */
5174     if (RxContext->Create.EaLength != 0)
5175     {
5176         UNIMPLEMENTED;
5177     }
5178 
5179     if (RxContext->Create.NtCreateParameters.DfsNameContext != NULL &&
5180         ((PDFS_NAME_CONTEXT)RxContext->Create.NtCreateParameters.DfsNameContext)->NameContextType == 0xAAAAAAAA)
5181     {
5182         CscAgent = TRUE;
5183     }
5184 
5185     return CscAgent;
5186 }
5187 
5188 VOID
5189 RxLockUserBuffer(
5190     IN PRX_CONTEXT RxContext,
5191     IN LOCK_OPERATION Operation,
5192     IN ULONG BufferLength)
5193 {
5194     PIRP Irp;
5195     PMDL Mdl = NULL;
5196 
5197     PAGED_CODE();
5198 
5199     _SEH2_TRY
5200     {
5201         Irp = RxContext->CurrentIrp;
5202         /* If we already have a MDL, make sure it's locked */
5203         if (Irp->MdlAddress != NULL)
5204         {
5205             ASSERT(RxLowIoIsMdlLocked(Irp->MdlAddress));
5206         }
5207         else
5208         {
5209             /* That likely means the driver asks for buffered IOs - we don't support it! */
5210             ASSERT(!BooleanFlagOn(Irp->Flags, IRP_INPUT_OPERATION));
5211 
5212             /* If we have a real length */
5213             if (BufferLength > 0)
5214             {
5215                 /* Allocate a MDL and lock it */
5216                 Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
5217                 if (Mdl == NULL)
5218                 {
5219                     RxContext->StoredStatus = STATUS_INSUFFICIENT_RESOURCES;
5220                     ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
5221                 }
5222 
5223                 MmProbeAndLockPages(Mdl, Irp->RequestorMode, Operation);
5224             }
5225         }
5226     }
5227     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5228     {
5229         NTSTATUS Status;
5230 
5231         Status = _SEH2_GetExceptionCode();
5232 
5233         /* Free the possible MDL we have allocated */
5234         IoFreeMdl(Mdl);
5235         Irp->MdlAddress = NULL;
5236 
5237         RxContext->Flags |= RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT;
5238 
5239         /* Fix status */
5240         if (!FsRtlIsNtstatusExpected(Status))
5241         {
5242             Status = STATUS_INVALID_USER_BUFFER;
5243         }
5244 
5245         RxContext->IoStatusBlock.Status = Status;
5246         ExRaiseStatus(Status);
5247     }
5248     _SEH2_END;
5249 }
5250 
5251 /*
5252  * @implemented
5253  */
5254 NTSTATUS
5255 RxLowIoCompletionTail(
5256     IN PRX_CONTEXT RxContext)
5257 {
5258     NTSTATUS Status;
5259     USHORT Operation;
5260 
5261     PAGED_CODE();
5262 
5263     DPRINT("RxLowIoCompletionTail(%p)\n", RxContext);
5264 
5265     /* Only continue if we're at APC_LEVEL or lower */
5266     if (RxShouldPostCompletion() &&
5267         !BooleanFlagOn(RxContext->LowIoContext.Flags, LOWIO_CONTEXT_FLAG_CAN_COMPLETE_AT_DPC_LEVEL))
5268     {
5269         return STATUS_MORE_PROCESSING_REQUIRED;
5270     }
5271 
5272     /* Call the completion routine */
5273     DPRINT("Calling completion routine: %p\n", RxContext->LowIoContext.CompletionRoutine);
5274     Status = RxContext->LowIoContext.CompletionRoutine(RxContext);
5275     if (Status == STATUS_MORE_PROCESSING_REQUIRED || Status == STATUS_RETRY)
5276     {
5277         return Status;
5278     }
5279 
5280     /* If it was a RW operation, for a paging file ... */
5281     Operation = RxContext->LowIoContext.Operation;
5282     if (Operation == LOWIO_OP_READ || Operation == LOWIO_OP_WRITE)
5283     {
5284         /* Remove ourselves from the list and resume operations */
5285         if (BooleanFlagOn(RxContext->LowIoContext.ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO))
5286         {
5287             ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
5288             RemoveEntryList(&RxContext->RxContextSerializationQLinks);
5289             RxContext->RxContextSerializationQLinks.Flink = NULL;
5290             RxContext->RxContextSerializationQLinks.Blink = NULL;
5291             ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
5292             RxResumeBlockedOperations_ALL(RxContext);
5293         }
5294     }
5295     else
5296     {
5297         /* Sanity check: we had known operation */
5298         ASSERT(Operation < LOWIO_OP_MAXIMUM);
5299     }
5300 
5301     /* If not sync operation, complete now. Otherwise, caller has already completed */
5302     if (!BooleanFlagOn(RxContext->LowIoContext.Flags, LOWIO_CONTEXT_FLAG_SYNCCALL))
5303     {
5304         RxCompleteRequest(RxContext, Status);
5305     }
5306 
5307     DPRINT("Status: %x\n", Status);
5308     return Status;
5309 }
5310 
5311 /*
5312  * @implemented
5313  */
5314 NTSTATUS
5315 NTAPI
5316 RxLowIoPopulateFsctlInfo(
5317     IN PRX_CONTEXT RxContext)
5318 {
5319     PMDL Mdl;
5320     PIRP Irp;
5321     UCHAR Method;
5322     PIO_STACK_LOCATION Stack;
5323 
5324     PAGED_CODE();
5325 
5326     DPRINT("RxLowIoPopulateFsctlInfo(%p)\n", RxContext);
5327 
5328     Irp = RxContext->CurrentIrp;
5329     Stack = RxContext->CurrentIrpSp;
5330 
5331     /* Copy stack parameters */
5332     RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode = Stack->Parameters.FileSystemControl.FsControlCode;
5333     RxContext->LowIoContext.ParamsFor.FsCtl.InputBufferLength = Stack->Parameters.FileSystemControl.InputBufferLength;
5334     RxContext->LowIoContext.ParamsFor.FsCtl.OutputBufferLength = Stack->Parameters.FileSystemControl.OutputBufferLength;
5335     RxContext->LowIoContext.ParamsFor.FsCtl.MinorFunction = Stack->MinorFunction;
5336     Method = METHOD_FROM_CTL_CODE(RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode);
5337 
5338     /* Same buffer in case of buffered */
5339     if (Method == METHOD_BUFFERED)
5340     {
5341         RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
5342         RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = Irp->AssociatedIrp.SystemBuffer;
5343 
5344         return STATUS_SUCCESS;
5345     }
5346 
5347     /* Two buffers for neither */
5348     if (Method == METHOD_NEITHER)
5349     {
5350         RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Stack->Parameters.FileSystemControl.Type3InputBuffer;
5351         RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = Irp->UserBuffer;
5352 
5353         return STATUS_SUCCESS;
5354     }
5355 
5356     /* Only IN/OUT remain */
5357     ASSERT(Method == METHOD_IN_DIRECT || Method == METHOD_OUT_DIRECT);
5358 
5359     /* Use system buffer for input */
5360     RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
5361     /* And MDL for output */
5362     Mdl = Irp->MdlAddress;
5363     if (Mdl != NULL)
5364     {
5365         RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
5366         if (RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer == NULL)
5367         {
5368             return STATUS_INSUFFICIENT_RESOURCES;
5369         }
5370     }
5371     else
5372     {
5373         RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = NULL;
5374     }
5375 
5376     return STATUS_SUCCESS;
5377 }
5378 
5379 NTSTATUS
5380 NTAPI
5381 RxLowIoSubmit(
5382     IN PRX_CONTEXT RxContext,
5383     IN PLOWIO_COMPLETION_ROUTINE CompletionRoutine)
5384 {
5385     NTSTATUS Status;
5386     USHORT Operation;
5387     BOOLEAN Synchronous;
5388     PLOWIO_CONTEXT LowIoContext;
5389 
5390     DPRINT("RxLowIoSubmit(%p, %p)\n", RxContext, CompletionRoutine);
5391 
5392     PAGED_CODE();
5393 
5394     LowIoContext = &RxContext->LowIoContext;
5395     Synchronous = !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION);
5396 
5397     LowIoContext->CompletionRoutine = CompletionRoutine;
5398 
5399     Status = STATUS_SUCCESS;
5400     Operation = LowIoContext->Operation;
5401     switch (Operation)
5402     {
5403         case LOWIO_OP_READ:
5404         case LOWIO_OP_WRITE:
5405             /* Check that the parameters were properly set by caller
5406              * See comment in RxInitializeLowIoContext()
5407              */
5408             ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteOffset != 0xFFFFFFEE);
5409             ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteCount != 0xEEEEEEEE);
5410 
5411             /* Lock the buffer */
5412             RxLockUserBuffer(RxContext,
5413                              (Operation == LOWIO_OP_READ ? IoWriteAccess : IoReadAccess),
5414                              LowIoContext->ParamsFor.ReadWrite.ByteCount);
5415             if (RxNewMapUserBuffer(RxContext) == NULL)
5416             {
5417                 return STATUS_INSUFFICIENT_RESOURCES;
5418             }
5419             LowIoContext->ParamsFor.ReadWrite.Buffer = RxContext->CurrentIrp->MdlAddress;
5420 
5421             /* If that's a paging IO, initialize serial operation */
5422             if (BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO))
5423             {
5424                 PFCB Fcb;
5425 
5426                 Fcb = (PFCB)RxContext->pFcb;
5427 
5428                 ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
5429                 RxContext->BlockedOpsMutex = &RxLowIoPagingIoSyncMutex;
5430                 if (Operation == LOWIO_OP_READ)
5431                 {
5432                     InsertTailList(&Fcb->Specific.Fcb.PagingIoReadsOutstanding, &RxContext->RxContextSerializationQLinks);
5433                 }
5434                 else
5435                 {
5436                     InsertTailList(&Fcb->Specific.Fcb.PagingIoWritesOutstanding, &RxContext->RxContextSerializationQLinks);
5437                 }
5438 
5439                 ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
5440             }
5441 
5442             break;
5443 
5444         case LOWIO_OP_FSCTL:
5445         case LOWIO_OP_IOCTL:
5446             /* Set FSCTL/IOCTL parameters */
5447             Status = RxLowIoPopulateFsctlInfo(RxContext);
5448             /* Check whether we're consistent: a length means a buffer */
5449             if (NT_SUCCESS(Status))
5450             {
5451                 if ((LowIoContext->ParamsFor.FsCtl.InputBufferLength > 0 &&
5452                      LowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL) ||
5453                     (LowIoContext->ParamsFor.FsCtl.OutputBufferLength > 0 &&
5454                      LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL))
5455                 {
5456                     Status = STATUS_INVALID_PARAMETER;
5457                 }
5458             }
5459             break;
5460 
5461         /* Nothing to do */
5462         case LOWIO_OP_SHAREDLOCK:
5463         case LOWIO_OP_EXCLUSIVELOCK:
5464         case LOWIO_OP_UNLOCK:
5465         case LOWIO_OP_UNLOCK_MULTIPLE:
5466         case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
5467         case LOWIO_OP_CLEAROUT:
5468             break;
5469 
5470         default:
5471             ASSERT(FALSE);
5472             Status = STATUS_INVALID_PARAMETER;
5473             break;
5474     }
5475 
5476     /* No need to perform extra init in case of posting */
5477     RxContext->Flags |= RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED;
5478 
5479     /* Preflight checks were OK, time to submit */
5480     if (NT_SUCCESS(Status))
5481     {
5482         PMINIRDR_DISPATCH Dispatch;
5483 
5484         if (!Synchronous)
5485         {
5486             InterlockedIncrement((volatile long *)&RxContext->ReferenceCount);
5487             /* If not synchronous, we're likely to return before the operation is finished */
5488             if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP))
5489             {
5490                 IoMarkIrpPending(RxContext->CurrentIrp);
5491             }
5492         }
5493 
5494         Dispatch = RxContext->RxDeviceObject->Dispatch;
5495         if (Dispatch != NULL)
5496         {
5497             /* We'll try to execute until the mini-rdr doesn't return pending */
5498             do
5499             {
5500                 RxContext->IoStatusBlock.Information = 0;
5501 
5502                 MINIRDR_CALL(Status, RxContext, Dispatch, MRxLowIOSubmit[Operation], (RxContext));
5503                 if (Status == STATUS_PENDING)
5504                 {
5505                     /* Unless it's not synchronous, caller will be happy with pending op */
5506                     if (!Synchronous)
5507                     {
5508                         return Status;
5509                     }
5510 
5511                     RxWaitSync(RxContext);
5512                     Status = RxContext->IoStatusBlock.Status;
5513                 }
5514                 else
5515                 {
5516                     if (!Synchronous)
5517                     {
5518                         /* We had marked the IRP pending, whereas the operation finished, drop that */
5519                         if (Status != STATUS_RETRY)
5520                         {
5521                             if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP))
5522                             {
5523                                 RxContext->CurrentIrpSp->Flags &= ~SL_PENDING_RETURNED;
5524                             }
5525 
5526                             InterlockedDecrement((volatile long *)&RxContext->ReferenceCount);
5527                         }
5528                     }
5529                 }
5530             } while (Status == STATUS_PENDING);
5531         }
5532         else
5533         {
5534             Status = STATUS_INVALID_PARAMETER;
5535         }
5536     }
5537 
5538     /* Call completion and return */
5539     RxContext->IoStatusBlock.Status = Status;
5540     LowIoContext->Flags |= LOWIO_CONTEXT_FLAG_SYNCCALL;
5541     return RxLowIoCompletionTail(RxContext);
5542 }
5543 
5544 /*
5545  * @implemented
5546  */
5547 PVOID
5548 RxMapSystemBuffer(
5549     IN PRX_CONTEXT RxContext)
5550 {
5551     PIRP Irp;
5552 
5553     PAGED_CODE();
5554 
5555     Irp = RxContext->CurrentIrp;
5556     /* We should have a MDL (buffered IOs are not supported!) */
5557     if (Irp->MdlAddress != NULL)
5558     {
5559         ASSERT(FALSE);
5560         return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
5561     }
5562 
5563     /* Just return system buffer */
5564     return Irp->AssociatedIrp.SystemBuffer;
5565 }
5566 
5567 /*
5568  * @implemented
5569  */
5570 VOID
5571 RxMarkFobxOnCleanup(
5572     PFOBX pFobx,
5573     PBOOLEAN NeedPurge)
5574 {
5575     PFCB Fcb;
5576     PFOBX ScavengerFobx;
5577     LARGE_INTEGER TickCount;
5578     PRDBSS_SCAVENGER Scavenger;
5579 
5580     PAGED_CODE();
5581 
5582     /* No FOBX, nothing to mark */
5583     if (pFobx == NULL)
5584     {
5585         return;
5586     }
5587 
5588     /* Query time for close */
5589     KeQueryTickCount(&TickCount);
5590 
5591     Fcb = (PFCB)pFobx->pSrvOpen->pFcb;
5592     ASSERT(NodeTypeIsFcb(Fcb));
5593 
5594     Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
5595     RxAcquireScavengerMutex();
5596 
5597     ScavengerFobx = NULL;
5598     /* If that's not a file, or even not a disk resource, just mark as dormant */
5599     if (NodeType(Fcb) != RDBSS_NTC_STORAGE_TYPE_FILE || Fcb->VNetRoot->pNetRoot->DeviceType != FILE_DEVICE_DISK)
5600     {
5601         SetFlag(pFobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
5602         InitializeListHead(&pFobx->ClosePendingList);
5603         ++Scavenger->NumberOfDormantFiles;
5604     }
5605     else
5606     {
5607         ASSERT(Scavenger->NumberOfDormantFiles >= 0);
5608         /* If we're about to reach the maximum dormant of FOBX */
5609         if (Scavenger->NumberOfDormantFiles >= Scavenger->MaximumNumberOfDormantFiles)
5610         {
5611             /* This should never be wrong... */
5612             if (!IsListEmpty(&Scavenger->ClosePendingFobxsList))
5613             {
5614                 /* Then, take the first from the list (oldest) and save it for later purge */
5615                 ScavengerFobx = CONTAINING_RECORD(Scavenger->ClosePendingFobxsList.Flink, FOBX, ClosePendingList);
5616                 if (ScavengerFobx->pSrvOpen != NULL && ScavengerFobx->pSrvOpen->pFcb == RX_GET_MRX_FCB(Fcb))
5617                 {
5618                     *NeedPurge = TRUE;
5619                     ScavengerFobx = NULL;
5620                 }
5621                 else
5622                 {
5623                     RxReferenceNetFobx(ScavengerFobx);
5624                 }
5625             }
5626         }
5627 
5628         /* Mark ourselves as dormant */
5629         SetFlag(pFobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
5630         pFobx->CloseTime.QuadPart = TickCount.QuadPart;
5631 
5632         /* And insert us in the list of dormant files */
5633         InsertTailList(&Scavenger->ClosePendingFobxsList, &pFobx->ClosePendingList);
5634         /* If scavenger was inactive, start it */
5635         if (Scavenger->NumberOfDormantFiles++ == 0 && Scavenger->State == RDBSS_SCAVENGER_INACTIVE)
5636         {
5637             Scavenger->State = RDBSS_SCAVENGER_DORMANT;
5638             RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem, RxScavengerTimerRoutine,
5639                                       Fcb->RxDeviceObject, Scavenger->TimeLimit);
5640         }
5641     }
5642 
5643     RxReleaseScavengerMutex();
5644 
5645     /* If we had reached max */
5646     if (ScavengerFobx != NULL)
5647     {
5648         NTSTATUS Status;
5649 
5650         /* Purge the oldest FOBX */
5651         Status = RxPurgeFobxFromCache(ScavengerFobx);
5652         if (Status != STATUS_SUCCESS)
5653         {
5654             *NeedPurge = TRUE;
5655         }
5656     }
5657 }
5658 
5659 /*
5660  * @implemented
5661  */
5662 VOID
5663 RxMarkFobxOnClose(
5664     PFOBX Fobx)
5665 {
5666     PFCB Fcb;
5667     PRDBSS_SCAVENGER Scavenger;
5668 
5669     PAGED_CODE();
5670 
5671     /* No FOBX, nothing to mark */
5672     if (Fobx == NULL)
5673     {
5674         return;
5675     }
5676 
5677     Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
5678     ASSERT(NodeTypeIsFcb(Fcb));
5679 
5680     Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
5681 
5682     RxAcquireScavengerMutex();
5683     /* Only mark it if it was already marked as dormant */
5684     if (BooleanFlagOn(Fobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT))
5685     {
5686         /* If FCB wasn't already decrement, do it now */
5687         if (!Fobx->fOpenCountDecremented)
5688         {
5689             Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
5690             ASSERT(NodeTypeIsFcb(Fcb));
5691             InterlockedDecrement((volatile long *)&Fcb->OpenCount);
5692 
5693             Fobx->fOpenCountDecremented = TRUE;
5694         }
5695 
5696         /* We're no longer dormant */
5697         InterlockedDecrement(&Scavenger->NumberOfDormantFiles);
5698         ClearFlag(Fobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
5699     }
5700 
5701     /* If we were inserted in the scavenger, drop ourselves out */
5702     if (!IsListEmpty(&Fobx->ClosePendingList))
5703     {
5704         RemoveEntryList(&Fobx->ClosePendingList);
5705         InitializeListHead(&Fobx->ClosePendingList);
5706     }
5707 
5708     RxReleaseScavengerMutex();
5709 }
5710 
5711 /*
5712  * @implemented
5713  */
5714 PVOID
5715 RxNewMapUserBuffer(
5716     PRX_CONTEXT RxContext)
5717 {
5718     PIRP Irp;
5719 
5720     PAGED_CODE();
5721 
5722     Irp = RxContext->CurrentIrp;
5723     if (Irp->MdlAddress != NULL)
5724     {
5725         return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
5726     }
5727 
5728     return Irp->UserBuffer;
5729 }
5730 
5731 BOOLEAN
5732 NTAPI
5733 RxNoOpAcquire(
5734     IN PVOID Fcb,
5735     IN BOOLEAN Wait)
5736 {
5737     UNIMPLEMENTED;
5738     return FALSE;
5739 }
5740 
5741 VOID
5742 NTAPI
5743 RxNoOpRelease(
5744     IN PVOID Fcb)
5745 {
5746     UNIMPLEMENTED;
5747 }
5748 
5749 VOID
5750 RxOrphanThisFcb(
5751     PFCB Fcb)
5752 {
5753     UNIMPLEMENTED;
5754 }
5755 
5756 VOID
5757 RxOrphanSrvOpens(
5758     IN PV_NET_ROOT ThisVNetRoot)
5759 {
5760     PFCB Fcb;
5761     USHORT Bucket;
5762     PNET_ROOT NetRoot;
5763     PRX_FCB_TABLE FcbTable;
5764     PRX_PREFIX_TABLE PrefixTable;
5765 
5766     PAGED_CODE();
5767 
5768     /* Mailslot won't have any SRV_OPEN (to orphan) */
5769     NetRoot = (PNET_ROOT)ThisVNetRoot->pNetRoot;
5770     if (NetRoot->Type == NET_ROOT_MAILSLOT)
5771     {
5772         return;
5773     }
5774 
5775     PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
5776     ASSERT(RxIsPrefixTableLockExclusive(PrefixTable));
5777 
5778     FcbTable = &NetRoot->FcbTable;
5779     RxAcquireFcbTableLockExclusive(FcbTable, TRUE);
5780 
5781     _SEH2_TRY
5782     {
5783         /* Now, we'll browse all the FCBs attached, and orphan related SRV_OPENs */
5784         for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
5785         {
5786             PLIST_ENTRY BucketList, Entry;
5787 
5788             BucketList = &FcbTable->HashBuckets[Bucket];
5789             Entry = BucketList->Flink;
5790             while (Entry != BucketList)
5791             {
5792                 Fcb = CONTAINING_RECORD(Entry, FCB, FcbTableEntry.HashLinks);
5793                 Entry = Entry->Flink;
5794 
5795                 ASSERT(NodeTypeIsFcb(Fcb));
5796                 RxOrphanSrvOpensForThisFcb(Fcb, ThisVNetRoot, FALSE);
5797             }
5798         }
5799 
5800         /* Of course, don't forget about NULL-entry */
5801         if (FcbTable->TableEntryForNull != NULL)
5802         {
5803             Fcb = CONTAINING_RECORD(FcbTable->TableEntryForNull, FCB, FcbTableEntry.HashLinks);
5804             ASSERT(NodeTypeIsFcb(Fcb));
5805             RxOrphanSrvOpensForThisFcb(Fcb, ThisVNetRoot, FALSE);
5806         }
5807     }
5808     _SEH2_FINALLY
5809     {
5810         RxReleaseFcbTableLock(FcbTable);
5811     }
5812     _SEH2_END;
5813 }
5814 
5815 VOID
5816 RxOrphanSrvOpensForThisFcb(
5817     IN PFCB Fcb,
5818     IN PV_NET_ROOT ThisVNetRoot,
5819     IN BOOLEAN OrphanAll)
5820 {
5821     UNIMPLEMENTED;
5822 }
5823 
5824 /*
5825  * @implemented
5826  */
5827 BOOLEAN
5828 RxpAcquirePrefixTableLockShared(
5829    PRX_PREFIX_TABLE pTable,
5830    BOOLEAN Wait,
5831    BOOLEAN ProcessBufferingStateChangeRequests)
5832 {
5833     PAGED_CODE();
5834 
5835     DPRINT("RxpAcquirePrefixTableLockShared(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
5836            pTable->TableLock.ActiveEntries);
5837 
5838     return ExAcquireResourceSharedLite(&pTable->TableLock, Wait);
5839 }
5840 
5841 /*
5842  * @implemented
5843  */
5844 BOOLEAN
5845 RxpAcquirePrefixTableLockExclusive(
5846    PRX_PREFIX_TABLE pTable,
5847    BOOLEAN Wait,
5848    BOOLEAN ProcessBufferingStateChangeRequests)
5849 {
5850     PAGED_CODE();
5851 
5852     DPRINT("RxpAcquirePrefixTableLockExclusive(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
5853            pTable->TableLock.ActiveEntries);
5854 
5855     return ExAcquireResourceExclusiveLite(&pTable->TableLock, Wait);
5856 }
5857 
5858 /*
5859  * @implemented
5860  */
5861 BOOLEAN
5862 RxpDereferenceAndFinalizeNetFcb(
5863     OUT PFCB ThisFcb,
5864     IN PRX_CONTEXT RxContext,
5865     IN BOOLEAN RecursiveFinalize,
5866     IN BOOLEAN ForceFinalize)
5867 {
5868     NTSTATUS Status;
5869     ULONG References;
5870     PNET_ROOT NetRoot;
5871     BOOLEAN ResourceAcquired, NetRootReferenced, Freed;
5872 
5873     PAGED_CODE();
5874 
5875     ASSERT(!ForceFinalize);
5876     ASSERT(NodeTypeIsFcb(ThisFcb));
5877     ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
5878 
5879     /* Unless we're recursively finalizing, or forcing, if FCB is still in use, quit */
5880     References = InterlockedDecrement((volatile long *)&ThisFcb->NodeReferenceCount);
5881     if (!ForceFinalize && !RecursiveFinalize && (ThisFcb->OpenCount != 0 || ThisFcb->UncleanCount != 0 || References > 1))
5882     {
5883         return FALSE;
5884     }
5885 
5886     Freed = FALSE;
5887     Status = STATUS_SUCCESS;
5888     NetRoot = (PNET_ROOT)ThisFcb->VNetRoot->pNetRoot;
5889     ResourceAcquired = FALSE;
5890     NetRootReferenced = FALSE;
5891     /* If FCB isn't orphaned, it still have context attached */
5892     if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_ORPHANED))
5893     {
5894         /* Don't let NetRoot go away before we're done */
5895         RxReferenceNetRoot(NetRoot);
5896         NetRootReferenced = TRUE;
5897 
5898         /* Try to acquire the table lock exclusively */
5899         if (!RxIsFcbTableLockExclusive(&NetRoot->FcbTable))
5900         {
5901             RxReferenceNetFcb(ThisFcb);
5902 
5903             if (!RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, FALSE))
5904             {
5905                 if (RxContext != NULL && RxContext != CHANGE_BUFFERING_STATE_CONTEXT &&
5906                     RxContext != CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
5907                 {
5908                     RxContext->Flags |= RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK;
5909                 }
5910 
5911                 RxReleaseFcb(RxContext, ThisFcb);
5912 
5913                 RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
5914 
5915                 Status = RxAcquireExclusiveFcb(RxContext, ThisFcb);
5916             }
5917 
5918             References = RxDereferenceNetFcb(ThisFcb);
5919 
5920             ResourceAcquired = TRUE;
5921         }
5922     }
5923 
5924     /* If locking was OK (or not needed!), attempt finalization */
5925     if (Status == STATUS_SUCCESS)
5926     {
5927         Freed = RxFinalizeNetFcb(ThisFcb, RecursiveFinalize, ForceFinalize, References);
5928     }
5929 
5930     /* Release table lock if acquired */
5931     if (ResourceAcquired)
5932     {
5933         RxReleaseFcbTableLock(&NetRoot->FcbTable);
5934     }
5935 
5936     /* We don't need the NetRoot anylonger */
5937     if (NetRootReferenced)
5938     {
5939         RxDereferenceNetRoot(NetRoot, LHS_LockNotHeld);
5940     }
5941 
5942     return Freed;
5943 }
5944 
5945 /*
5946  * @implemented
5947  */
5948 LONG
5949 RxpDereferenceNetFcb(
5950    PFCB Fcb)
5951 {
5952     LONG NewCount;
5953 
5954     PAGED_CODE();
5955 
5956     ASSERT(NodeTypeIsFcb(Fcb));
5957 
5958     NewCount = InterlockedDecrement((volatile long *)&Fcb->NodeReferenceCount);
5959     ASSERT(NewCount >= 0);
5960 
5961     PRINT_REF_COUNT(NETFCB, NewCount);
5962 
5963     return NewCount;
5964 }
5965 
5966 /*
5967  * @implemented
5968  */
5969 VOID
5970 NTAPI
5971 RxpDestroySrvCall(
5972     IN PVOID Context)
5973 {
5974     NTSTATUS Status;
5975     PSRV_CALL SrvCall;
5976     BOOLEAN ForceFinalize;
5977     PRX_PREFIX_TABLE PrefixTable;
5978 
5979     SrvCall = (PSRV_CALL)Context;
5980     /* At this step, RxFinalizeSrvCall already cleaned some fields */
5981     ASSERT(SrvCall->UpperFinalizationDone);
5982 
5983     PrefixTable = SrvCall->RxDeviceObject->pRxNetNameTable;
5984     /* Were we called with ForceFinalize? */
5985     ForceFinalize = BooleanFlagOn(SrvCall->Flags, SRVCALL_FLAG_FORCE_FINALIZED);
5986 
5987     /* Notify mini-rdr */
5988     MINIRDR_CALL_THROUGH(Status, SrvCall->RxDeviceObject->Dispatch,
5989                          MRxFinalizeSrvCall, ((PMRX_SRV_CALL)SrvCall,
5990                          ForceFinalize));
5991     (void)Status;
5992 
5993     /* Dereference our extra reference (set before queueing) */
5994     RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
5995     InterlockedDecrement((volatile long *)&SrvCall->NodeReferenceCount);
5996     /* And finalize for real, with the right context */
5997     RxFinalizeSrvCall(SrvCall, FALSE, ForceFinalize);
5998     RxReleasePrefixTableLock(PrefixTable);
5999 }
6000 
6001 /*
6002  * @implemented
6003  */
6004 VOID
6005 RxpDiscardChangeBufferingStateRequests(
6006     _Inout_ PLIST_ENTRY DiscardedRequests)
6007 {
6008     PLIST_ENTRY Entry;
6009 
6010     PAGED_CODE();
6011 
6012     /* No requests to discard */
6013     if (IsListEmpty(DiscardedRequests))
6014     {
6015         return;
6016     }
6017 
6018     /* Free all the discarded requests */
6019     Entry = DiscardedRequests->Flink;
6020     while (Entry != DiscardedRequests)
6021     {
6022         PCHANGE_BUFFERING_STATE_REQUEST Request;
6023 
6024         Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
6025         Entry = Entry->Flink;
6026 
6027         DPRINT("Req %p for %p (%p) discarded\n", Request, Request->SrvOpenKey, Request->SrvOpen);
6028 
6029         RxPrepareRequestForReuse(Request);
6030         RxFreePool(Request);
6031     }
6032 }
6033 
6034 /*
6035  * @implemented
6036  */
6037 VOID
6038 RxpDispatchChangeBufferingStateRequests(
6039     PSRV_CALL SrvCall,
6040     PSRV_OPEN SrvOpen,
6041     PLIST_ENTRY DiscardedRequests)
6042 {
6043     KIRQL OldIrql;
6044     NTSTATUS Status;
6045     BOOLEAN StartDispatcher;
6046     LIST_ENTRY AcceptedReqs;
6047     LIST_ENTRY DispatcherList;
6048     PRX_BUFFERING_MANAGER BufferingManager;
6049 
6050     /* Initialize our lists */
6051     InitializeListHead(&AcceptedReqs);
6052     InitializeListHead(DiscardedRequests);
6053 
6054     /* Transfer the requests to dispatch locally */
6055     BufferingManager = &SrvCall->BufferingManager;
6056     KeAcquireSpinLock(&BufferingManager->SpinLock, &OldIrql);
6057     RxTransferList(&DispatcherList, &BufferingManager->DispatcherList);
6058     KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
6059 
6060     /* If there were requests */
6061     if (!IsListEmpty(&DispatcherList))
6062     {
6063         PLIST_ENTRY Entry;
6064 
6065         /* For each of the entries... */
6066         Entry = DispatcherList.Flink;
6067         while (Entry != &DispatcherList)
6068         {
6069             PCHANGE_BUFFERING_STATE_REQUEST Request;
6070 
6071             Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
6072             Entry = Entry->Flink;
6073 
6074             /* If we have been provided a SRV_OPEN, see whether it matches */
6075             if (SrvOpen != NULL)
6076             {
6077                 /* Match, the request is accepted */
6078                 if (Request->SrvOpenKey == SrvOpen->Key)
6079                 {
6080                     Request->SrvOpen = SrvOpen;
6081                     RxReferenceSrvOpen(SrvOpen);
6082 
6083                     RemoveEntryList(&Request->ListEntry);
6084                     InsertTailList(&AcceptedReqs, &Request->ListEntry);
6085 
6086                     /* Move to the next entry */
6087                     continue;
6088                 }
6089                 else
6090                 {
6091                     Status = STATUS_PENDING;
6092                 }
6093             }
6094             else
6095             {
6096                 /* No SRV_OPEN provided, try to find one */
6097                 Status = RxpLookupSrvOpenForRequestLite(SrvCall, Request);
6098             }
6099 
6100             /* We found a matching SRV_OPEN, accept the request */
6101             if (Status == STATUS_SUCCESS)
6102             {
6103                 RemoveEntryList(&Request->ListEntry);
6104                 InsertTailList(&AcceptedReqs, &Request->ListEntry);
6105             }
6106             /* Another run might help handling it, don't discard it */
6107             else if (Status == STATUS_PENDING)
6108             {
6109                 continue;
6110             }
6111             /* Otherwise, discard the request */
6112             else
6113             {
6114                 ASSERT(Status == STATUS_NOT_FOUND);
6115 
6116                 RemoveEntryList(&Request->ListEntry);
6117                 InsertTailList(DiscardedRequests, &Request->ListEntry);
6118             }
6119         }
6120     }
6121 
6122     KeAcquireSpinLock(&BufferingManager->SpinLock, &OldIrql);
6123     /* Nothing to dispatch, no need to start dispatcher */
6124     if (IsListEmpty(&DispatcherList))
6125     {
6126         StartDispatcher = FALSE;
6127     }
6128     else
6129     {
6130         /* Transfer back the list of the not treated entries to the buffering manager */
6131         RxTransferList(&BufferingManager->DispatcherList, &DispatcherList);
6132         StartDispatcher = (BufferingManager->DispatcherActive == FALSE);
6133         /* If the dispatcher isn't active, start it */
6134         if (StartDispatcher)
6135         {
6136             BufferingManager->DispatcherActive = TRUE;
6137         }
6138     }
6139 
6140     /* If there were accepted requests, move them to the buffering manager */
6141     if (!IsListEmpty(&AcceptedReqs))
6142     {
6143         RxTransferList(&BufferingManager->HandlerList, &AcceptedReqs);
6144     }
6145     KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
6146 
6147     /* If we're to start the dispatcher, do it */
6148     if (StartDispatcher)
6149     {
6150         RxReferenceSrvCall(SrvCall);
6151         DPRINT("Starting dispatcher\n");
6152         RxPostToWorkerThread(RxFileSystemDeviceObject, HyperCriticalWorkQueue,
6153                              &BufferingManager->DispatcherWorkItem,
6154                              RxDispatchChangeBufferingStateRequests, SrvCall);
6155     }
6156 }
6157 
6158 /*
6159  * @implemented
6160  */
6161 NTSTATUS
6162 RxpLookupSrvOpenForRequestLite(
6163     IN PSRV_CALL SrvCall,
6164     IN OUT PCHANGE_BUFFERING_STATE_REQUEST Request)
6165 {
6166     NTSTATUS Status;
6167     PLIST_ENTRY Entry;
6168     PSRV_OPEN SrvOpen;
6169 
6170     PAGED_CODE();
6171 
6172     Status = STATUS_SUCCESS;
6173     /* Browse all our associated SRV_OPENs to find the one! */
6174     for (Entry = SrvCall->BufferingManager.SrvOpenLists[0].Flink;
6175          Entry != &SrvCall->BufferingManager.SrvOpenLists[0];
6176          Entry = Entry->Flink)
6177     {
6178         /* Same key, not orphaned, this is ours */
6179         SrvOpen = CONTAINING_RECORD(Entry, SRV_OPEN, SrvOpenKeyList);
6180         if (SrvOpen->Key == Request->SrvOpenKey)
6181         {
6182             if (!BooleanFlagOn(SrvOpen->pFcb->FcbState, FCB_STATE_ORPHANED))
6183             {
6184                 RxReferenceSrvOpen(SrvOpen);
6185                 break;
6186             }
6187         }
6188     }
6189 
6190     /* We didn't manage to find a SRV_OPEN */
6191     if (Entry == &SrvCall->BufferingManager.SrvOpenLists[0])
6192     {
6193         SrvOpen = NULL;
6194 
6195         /* The coming open might help, mark as pending for later retry */
6196         if (SrvCall->BufferingManager.NumberOfOutstandingOpens != 0)
6197         {
6198             Status = STATUS_PENDING;
6199         }
6200         /* Else, it's a complete failure */
6201         else
6202         {
6203             Status = STATUS_NOT_FOUND;
6204         }
6205     }
6206 
6207     /* Return the (not) found SRV_OPEN */
6208     Request->SrvOpen = SrvOpen;
6209 
6210     return Status;
6211 }
6212 
6213 /*
6214  * @implemented
6215  */
6216 VOID
6217 RxpMarkInstanceForScavengedFinalization(
6218    PVOID Instance)
6219 {
6220     NODE_TYPE_CODE NodeType;
6221     PNODE_TYPE_AND_SIZE Node;
6222     PRDBSS_SCAVENGER Scavenger;
6223     PRDBSS_DEVICE_OBJECT DeviceObject;
6224     PLIST_ENTRY ScavengerHead, InstEntry;
6225 
6226     PAGED_CODE();
6227 
6228     /* If still referenced, don't mark it (broken caller) */
6229     Node = (PNODE_TYPE_AND_SIZE)Instance;
6230     if (Node->NodeReferenceCount > 1)
6231     {
6232         return;
6233     }
6234 
6235     DeviceObject = RxGetDeviceObjectOfInstance(Instance);
6236     Scavenger = DeviceObject->pRdbssScavenger;
6237 
6238     /* Mark the node */
6239     NodeType = NodeType(Instance);
6240     SetFlag(NodeType(Node), RX_SCAVENGER_MASK);
6241     DPRINT("Node %p has now the scavenger mark!\n", Instance);
6242 
6243     /* Increase the count in the scavenger, and queue it */
6244     ScavengerHead = NULL;
6245     switch (NodeType)
6246     {
6247         case RDBSS_NTC_FOBX:
6248             ++Scavenger->FobxsToBeFinalized;
6249             ScavengerHead = &Scavenger->FobxFinalizationList;
6250             InstEntry = &((PFOBX)Instance)->ScavengerFinalizationList;
6251             break;
6252 
6253         case RDBSS_NTC_SRVCALL:
6254             ++Scavenger->SrvCallsToBeFinalized;
6255             ScavengerHead = &Scavenger->SrvCallFinalizationList;
6256             InstEntry = &((PSRV_CALL)Instance)->ScavengerFinalizationList;
6257             break;
6258 
6259         case RDBSS_NTC_NETROOT:
6260             ++Scavenger->NetRootsToBeFinalized;
6261             ScavengerHead = &Scavenger->NetRootFinalizationList;
6262             InstEntry = &((PNET_ROOT)Instance)->ScavengerFinalizationList;
6263             break;
6264 
6265         case RDBSS_NTC_V_NETROOT:
6266             ++Scavenger->VNetRootsToBeFinalized;
6267             ScavengerHead = &Scavenger->VNetRootFinalizationList;
6268             InstEntry = &((PV_NET_ROOT)Instance)->ScavengerFinalizationList;
6269             break;
6270 
6271         case RDBSS_NTC_SRVOPEN:
6272             ++Scavenger->SrvOpensToBeFinalized;
6273             ScavengerHead = &Scavenger->SrvOpenFinalizationList;
6274             InstEntry = &((PSRV_OPEN)Instance)->ScavengerFinalizationList;
6275             break;
6276     }
6277 
6278     /* Extra ref for scavenger */
6279     InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
6280 
6281     /* If matching type */
6282     if (ScavengerHead != NULL)
6283     {
6284         /* Insert in the scavenger list */
6285         InsertTailList(ScavengerHead, InstEntry);
6286 
6287         /* And if it wasn't started, start it */
6288         if (Scavenger->State == RDBSS_SCAVENGER_INACTIVE)
6289         {
6290             Scavenger->State = RDBSS_SCAVENGER_DORMANT;
6291             RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem,
6292                                       RxScavengerTimerRoutine, DeviceObject, Scavenger->TimeLimit);
6293         }
6294     }
6295 }
6296 
6297 /*
6298  * @implemented
6299  */
6300 NTSTATUS
6301 NTAPI
6302 RxPostOneShotTimerRequest(
6303     IN PRDBSS_DEVICE_OBJECT pDeviceObject,
6304     IN PRX_WORK_ITEM pWorkItem,
6305     IN PRX_WORKERTHREAD_ROUTINE Routine,
6306     IN PVOID pContext,
6307     IN LARGE_INTEGER TimeInterval)
6308 {
6309     KIRQL OldIrql;
6310 
6311     ASSERT(pWorkItem != NULL);
6312 
6313     /* Prepare the work item */
6314     ExInitializeWorkItem(&pWorkItem->WorkQueueItem, Routine, pContext);
6315     pWorkItem->WorkQueueItem.pDeviceObject = pDeviceObject;
6316 
6317     /* Last tick can be computed with the number of times it was caller (timertickcount)
6318      * and the interval between calls
6319      */
6320     KeAcquireSpinLock(&RxTimerLock, &OldIrql);
6321     pWorkItem->LastTick = (TimeInterval.QuadPart / 550000) + RxTimerTickCount + 1;
6322     /* Insert in work queue */
6323     InsertTailList(&RxTimerQueueHead, &pWorkItem->WorkQueueItem.List);
6324     KeReleaseSpinLock(&RxTimerLock, OldIrql);
6325 
6326     /* If there are queued events, queue an execution */
6327     if (IsListEmpty(&RxTimerQueueHead))
6328     {
6329         KeSetTimer(&RxTimer, RxTimerInterval, &RxTimerDpc);
6330     }
6331 
6332     return STATUS_SUCCESS;
6333 }
6334 
6335 /*
6336  * @implemented
6337  */
6338 NTSTATUS
6339 NTAPI
6340 RxPostToWorkerThread(
6341     _In_ PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
6342     _In_ WORK_QUEUE_TYPE WorkQueueType,
6343     _In_ PRX_WORK_QUEUE_ITEM pWorkQueueItem,
6344     _In_ PRX_WORKERTHREAD_ROUTINE Routine,
6345     _In_ PVOID pContext)
6346 {
6347     /* Initialize work queue item */
6348     pWorkQueueItem->List.Flink = NULL;
6349     pWorkQueueItem->WorkerRoutine = Routine;
6350     pWorkQueueItem->Parameter = pContext;
6351 
6352     /* And insert it in the work queue */
6353     return RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, pWorkQueueItem);
6354 }
6355 
6356 VOID
6357 RxpProcessChangeBufferingStateRequests(
6358     PSRV_CALL SrvCall,
6359     BOOLEAN UpdateHandlerState)
6360 {
6361     UNIMPLEMENTED;
6362 }
6363 
6364 /*
6365  * @implemented
6366  */
6367 PRX_PREFIX_ENTRY
6368 RxPrefixTableInsertName(
6369     IN OUT PRX_PREFIX_TABLE ThisTable,
6370     IN OUT PRX_PREFIX_ENTRY ThisEntry,
6371     IN PVOID Container,
6372     IN PULONG ContainerRefCount,
6373     IN USHORT CaseInsensitiveLength,
6374     IN PRX_CONNECTION_ID ConnectionId
6375     )
6376 {
6377     PAGED_CODE();
6378 
6379     DPRINT("Insert: %wZ\n", &ThisEntry->Prefix);
6380 
6381     ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
6382     ASSERT(CaseInsensitiveLength <= ThisEntry->Prefix.Length);
6383 
6384     /* Copy parameters and compute hash */
6385     ThisEntry->CaseInsensitiveLength = CaseInsensitiveLength;
6386     ThisEntry->ContainingRecord = Container;
6387     ThisEntry->ContainerRefCount = ContainerRefCount;
6388     InterlockedIncrement((volatile long *)ContainerRefCount);
6389     ThisEntry->SavedHashValue = RxTableComputeHashValue(&ThisEntry->Prefix);
6390     DPRINT("Associated hash: %x\n", ThisEntry->SavedHashValue);
6391 
6392     /* If no path length: this is entry for null path */
6393     if (ThisEntry->Prefix.Length == 0)
6394     {
6395         ThisTable->TableEntryForNull = ThisEntry;
6396     }
6397     /* Otherwise, insert in the appropriate bucket */
6398     else
6399     {
6400         InsertTailList(HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue), &ThisEntry->HashLinks);
6401     }
6402 
6403     /* If we had a connection ID, keep track of it */
6404     if (ConnectionId != NULL)
6405     {
6406         ThisEntry->ConnectionId.Luid = ConnectionId->Luid;
6407     }
6408     else
6409     {
6410         ThisEntry->ConnectionId.Luid.LowPart = 0;
6411         ThisEntry->ConnectionId.Luid.HighPart = 0;
6412     }
6413 
6414     InsertTailList(&ThisTable->MemberQueue, &ThisEntry->MemberQLinks);
6415     /* Reflect the changes */
6416     ++ThisTable->Version;
6417 
6418     DPRINT("Inserted in bucket: %p\n", HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue));
6419 
6420     return ThisEntry;
6421 }
6422 
6423 /*
6424  * @implemented
6425  */
6426 PVOID
6427 RxPrefixTableLookupName(
6428     IN PRX_PREFIX_TABLE ThisTable,
6429     IN PUNICODE_STRING CanonicalName,
6430     OUT PUNICODE_STRING RemainingName,
6431     IN PRX_CONNECTION_ID ConnectionId)
6432 {
6433     PVOID Container;
6434 
6435     PAGED_CODE();
6436 
6437     ASSERT(RxIsPrefixTableLockAcquired(ThisTable));
6438     ASSERT(CanonicalName->Length > 0);
6439 
6440     /* Call the internal helper */
6441     Container = RxTableLookupName(ThisTable, CanonicalName, RemainingName, ConnectionId);
6442     if (Container == NULL)
6443     {
6444         return NULL;
6445     }
6446 
6447     /* Reference our container before returning it */
6448     if (RdbssReferenceTracingValue != 0)
6449     {
6450         NODE_TYPE_CODE Type;
6451 
6452         Type = (NodeType(Container) & ~RX_SCAVENGER_MASK);
6453         switch (Type)
6454         {
6455             case RDBSS_NTC_SRVCALL:
6456                 RxReferenceSrvCall(Container);
6457                 break;
6458 
6459             case RDBSS_NTC_NETROOT:
6460                 RxReferenceNetRoot(Container);
6461                 break;
6462 
6463             case RDBSS_NTC_V_NETROOT:
6464                 RxReferenceVNetRoot(Container);
6465                 break;
6466 
6467             default:
6468                 DPRINT1("Invalid node type: %x\n", Type);
6469                 ASSERT(FALSE);
6470                 RxReference(Container);
6471                 break;
6472         }
6473     }
6474     else
6475     {
6476         RxReference(Container);
6477     }
6478 
6479     return Container;
6480 }
6481 
6482 /*
6483  * @implemented
6484  */
6485 LONG
6486 RxpReferenceNetFcb(
6487    PFCB Fcb)
6488 {
6489     LONG NewCount;
6490 
6491     PAGED_CODE();
6492 
6493     ASSERT(NodeTypeIsFcb(Fcb));
6494 
6495     NewCount = InterlockedIncrement((volatile long *)&Fcb->NodeReferenceCount);
6496 
6497     PRINT_REF_COUNT(NETFCB, Fcb->NodeReferenceCount);
6498 
6499     return NewCount;
6500 }
6501 
6502 /*
6503  * @implemented
6504  */
6505 VOID
6506 RxpReleasePrefixTableLock(
6507    PRX_PREFIX_TABLE pTable,
6508    BOOLEAN ProcessBufferingStateChangeRequests)
6509 {
6510     PAGED_CODE();
6511 
6512     DPRINT("RxpReleasePrefixTableLock(%p, %d) -> %d\n", pTable, ProcessBufferingStateChangeRequests,
6513            pTable->TableLock.ActiveEntries);
6514 
6515     ExReleaseResourceLite(&pTable->TableLock);
6516 }
6517 
6518 /*
6519  * @implemented
6520  */
6521 VOID
6522 NTAPI
6523 RxPrepareContextForReuse(
6524    IN OUT PRX_CONTEXT RxContext)
6525 {
6526     PAGED_CODE();
6527 
6528     /* When we reach that point, make sure mandatory parts are null-ed */
6529     if (RxContext->MajorFunction == IRP_MJ_CREATE)
6530     {
6531         ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
6532         RxContext->Create.RdrFlags = 0;
6533     }
6534     else if (RxContext->MajorFunction == IRP_MJ_READ || RxContext->MajorFunction == IRP_MJ_WRITE)
6535     {
6536         ASSERT(RxContext->RxContextSerializationQLinks.Flink == NULL);
6537         ASSERT(RxContext->RxContextSerializationQLinks.Blink == NULL);
6538     }
6539 
6540     RxContext->ReferenceCount = 0;
6541 }
6542 
6543 /*
6544  * @implemented
6545  */
6546 VOID
6547 RxPrepareRequestForReuse(
6548     PCHANGE_BUFFERING_STATE_REQUEST Request)
6549 {
6550     PSRV_OPEN SrvOpen;
6551 
6552     PAGED_CODE();
6553 
6554     SrvOpen = Request->SrvOpen;
6555 
6556     /* If the request was already prepared for service */
6557     if (BooleanFlagOn(Request->Flags, RX_REQUEST_PREPARED_FOR_HANDLING))
6558     {
6559         /* We have to dereference the associated SRV_OPEN depending on the lock */
6560         if (RxIsFcbAcquiredExclusive(SrvOpen->pFcb))
6561         {
6562             RxDereferenceSrvOpen(SrvOpen, LHS_ExclusiveLockHeld);
6563         }
6564         else
6565         {
6566             RxDereferenceSrvOpen(SrvOpen, LHS_LockNotHeld);
6567         }
6568     }
6569     /* Otherwise, just dereference */
6570     else if (SrvOpen != NULL)
6571     {
6572         RxDereferenceSrvOpen(SrvOpen, LHS_LockNotHeld);
6573     }
6574 
6575     Request->SrvOpen = NULL;
6576 }
6577 
6578 /*
6579  * @implemented
6580  */
6581 VOID
6582 NTAPI
6583 RxProcessChangeBufferingStateRequests(
6584     _In_ PVOID SrvCall)
6585 {
6586     /* Call internal routine */
6587     RxUndoScavengerFinalizationMarking(SrvCall);
6588     RxpProcessChangeBufferingStateRequests(SrvCall, TRUE);
6589 }
6590 
6591 /*
6592  * @implemented
6593  */
6594 VOID
6595 RxProcessChangeBufferingStateRequestsForSrvOpen(
6596     PSRV_OPEN SrvOpen)
6597 {
6598     LONG NumberOfBufferingChangeRequests, LockedOldBufferingToken, OldBufferingToken;
6599 
6600     /* Get the current number of change requests */
6601     NumberOfBufferingChangeRequests = ((PSRV_CALL)SrvOpen->pVNetRoot->pNetRoot->pSrvCall)->BufferingManager.CumulativeNumberOfBufferingChangeRequests;
6602     /* Get our old token */
6603     OldBufferingToken = SrvOpen->BufferingToken;
6604     LockedOldBufferingToken = InterlockedCompareExchange(&SrvOpen->BufferingToken,
6605                                                          NumberOfBufferingChangeRequests,
6606                                                          NumberOfBufferingChangeRequests);
6607     /* If buffering state changed in between, process changes */
6608     if (OldBufferingToken != LockedOldBufferingToken)
6609     {
6610         PFCB Fcb;
6611         NTSTATUS Status;
6612 
6613         /* Acquire the FCB and start processing */
6614         Fcb = (PFCB)SrvOpen->pFcb;
6615         Status = RxAcquireExclusiveFcb(NULL, Fcb);
6616         if (Status == STATUS_SUCCESS)
6617         {
6618             RxProcessFcbChangeBufferingStateRequest(Fcb);
6619             RxReleaseFcb(NULL, Fcb);
6620         }
6621     }
6622 }
6623 
6624 VOID
6625 RxProcessFcbChangeBufferingStateRequest(
6626     PFCB Fcb)
6627 {
6628     UNIMPLEMENTED;
6629 }
6630 
6631 /*
6632  * @implemented
6633  */
6634 VOID
6635 RxpScavengeFobxs(
6636     PRDBSS_SCAVENGER Scavenger,
6637     PLIST_ENTRY FobxToScavenge)
6638 {
6639     /* Explore the whole list of FOBX to scavenge */
6640     while (!IsListEmpty(FobxToScavenge))
6641     {
6642         PFCB Fcb;
6643         PFOBX Fobx;
6644         PLIST_ENTRY Entry;
6645 
6646         Entry = RemoveHeadList(FobxToScavenge);
6647         Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList);
6648         Fcb = (PFCB)Fobx->SrvOpen->pFcb;
6649 
6650         /* Try to acquire the lock exclusively to perform finalization */
6651         if (RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS)
6652         {
6653             RxDereferenceNetRoot(Fobx, LHS_LockNotHeld);
6654         }
6655         else
6656         {
6657             RxReferenceNetFcb(Fcb);
6658             RxDereferenceNetRoot(Fobx, LHS_ExclusiveLockHeld);
6659 
6660             if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
6661             {
6662                 RxReleaseFcb(NULL, Fcb);
6663             }
6664         }
6665     }
6666 }
6667 
6668 BOOLEAN
6669 RxpTrackDereference(
6670     _In_ ULONG TraceType,
6671     _In_ PCSTR FileName,
6672     _In_ ULONG Line,
6673     _In_ PVOID Instance)
6674 {
6675     PCSTR InstanceType;
6676     ULONG ReferenceCount;
6677 
6678     PAGED_CODE();
6679 
6680     if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
6681     {
6682         return TRUE;
6683     }
6684 
6685     switch (TraceType)
6686     {
6687         case RDBSS_REF_TRACK_SRVCALL:
6688             InstanceType = "SrvCall";
6689             ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount;
6690             break;
6691 
6692         case RDBSS_REF_TRACK_NETROOT:
6693             InstanceType = "NetRoot";
6694             ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount;
6695             break;
6696 
6697         case RDBSS_REF_TRACK_VNETROOT:
6698             InstanceType = "VNetRoot";
6699             ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount;
6700             break;
6701 
6702         case RDBSS_REF_TRACK_NETFOBX:
6703             InstanceType = "NetFobx";
6704             ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount;
6705             break;
6706 
6707         case RDBSS_REF_TRACK_NETFCB:
6708             InstanceType = "NetFcb";
6709             ReferenceCount = ((PFCB)Instance)->NodeReferenceCount;
6710             break;
6711 
6712         case RDBSS_REF_TRACK_SRVOPEN:
6713             InstanceType = "SrvOpen";
6714             ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount;
6715             break;
6716 
6717         default:
6718             DPRINT1("Invalid node type!\n");
6719             return TRUE;
6720     }
6721 
6722     if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING))
6723     {
6724         UNIMPLEMENTED;
6725     }
6726 
6727     if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING))
6728     {
6729         DbgPrint("(%s:%d) %p (%s) dereferenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount);
6730     }
6731 
6732     return TRUE;
6733 }
6734 
6735 VOID
6736 RxpTrackReference(
6737     _In_ ULONG TraceType,
6738     _In_ PCSTR FileName,
6739     _In_ ULONG Line,
6740     _In_ PVOID Instance)
6741 {
6742     PCSTR InstanceType;
6743     ULONG ReferenceCount;
6744 
6745     if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
6746     {
6747         return;
6748     }
6749 
6750     switch (TraceType)
6751     {
6752         case RDBSS_REF_TRACK_SRVCALL:
6753             InstanceType = "SrvCall";
6754             ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount;
6755             break;
6756 
6757         case RDBSS_REF_TRACK_NETROOT:
6758             InstanceType = "NetRoot";
6759             ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount;
6760             break;
6761 
6762         case RDBSS_REF_TRACK_VNETROOT:
6763             InstanceType = "VNetRoot";
6764             ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount;
6765             break;
6766 
6767         case RDBSS_REF_TRACK_NETFOBX:
6768             InstanceType = "NetFobx";
6769             ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount;
6770             break;
6771 
6772         case RDBSS_REF_TRACK_NETFCB:
6773             InstanceType = "NetFcb";
6774             ReferenceCount = ((PFCB)Instance)->NodeReferenceCount;
6775             break;
6776 
6777         case RDBSS_REF_TRACK_SRVOPEN:
6778             InstanceType = "SrvOpen";
6779             ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount;
6780             break;
6781 
6782         default:
6783             DPRINT1("Invalid node type!\n");
6784             return;
6785     }
6786 
6787     if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING))
6788     {
6789         UNIMPLEMENTED;
6790     }
6791 
6792     if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING))
6793     {
6794         DbgPrint("(%s:%d) %p (%s) referenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount);
6795     }
6796 }
6797 
6798 /*
6799  * @implemented
6800  */
6801 VOID
6802 RxpUndoScavengerFinalizationMarking(
6803    PVOID Instance)
6804 {
6805     PLIST_ENTRY ListEntry;
6806     PNODE_TYPE_AND_SIZE Node;
6807     PRDBSS_SCAVENGER Scavenger;
6808 
6809     PAGED_CODE();
6810 
6811     Node = (PNODE_TYPE_AND_SIZE)Instance;
6812     /* There's no marking - nothing to do */
6813     if (!BooleanFlagOn(NodeType(Node), RX_SCAVENGER_MASK))
6814     {
6815         return;
6816     }
6817 
6818     /* First of all, remove the mark */
6819     ClearFlag(NodeType(Node), RX_SCAVENGER_MASK);
6820     DPRINT("Node %p no longer has the scavenger mark\n");
6821 
6822     /* And now, remove from the scavenger */
6823     Scavenger = RxGetDeviceObjectOfInstance(Instance)->pRdbssScavenger;
6824     switch (NodeType(Node))
6825     {
6826         case RDBSS_NTC_FOBX:
6827             --Scavenger->FobxsToBeFinalized;
6828             ListEntry = &((PFOBX)Instance)->ScavengerFinalizationList;
6829             break;
6830 
6831         case RDBSS_NTC_SRVCALL:
6832             --Scavenger->SrvCallsToBeFinalized;
6833             ListEntry = &((PSRV_CALL)Instance)->ScavengerFinalizationList;
6834             break;
6835 
6836         case RDBSS_NTC_NETROOT:
6837             --Scavenger->NetRootsToBeFinalized;
6838             ListEntry = &((PNET_ROOT)Instance)->ScavengerFinalizationList;
6839             break;
6840 
6841         case RDBSS_NTC_V_NETROOT:
6842             --Scavenger->VNetRootsToBeFinalized;
6843             ListEntry = &((PV_NET_ROOT)Instance)->ScavengerFinalizationList;
6844             break;
6845 
6846         case RDBSS_NTC_SRVOPEN:
6847             --Scavenger->SrvOpensToBeFinalized;
6848             ListEntry = &((PSRV_OPEN)Instance)->ScavengerFinalizationList;
6849             break;
6850 
6851         default:
6852             return;
6853     }
6854 
6855     /* Also, remove the extra ref from the scavenger */
6856     RemoveEntryList(ListEntry);
6857     InterlockedDecrement((volatile long *)&Node->NodeReferenceCount);
6858 }
6859 
6860 /*
6861  * @implemented
6862  */
6863 VOID
6864 RxPurgeChangeBufferingStateRequestsForSrvOpen(
6865     PSRV_OPEN SrvOpen)
6866 {
6867     PSRV_CALL SrvCall;
6868     LIST_ENTRY Discarded;
6869 
6870     PAGED_CODE();
6871 
6872     ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
6873 
6874     /* Initialize our discarded list */
6875     InitializeListHead(&Discarded);
6876 
6877     SrvCall = (PSRV_CALL)SrvOpen->Fcb->VNetRoot->pNetRoot->pSrvCall;
6878     RxAcquireBufferingManagerMutex(&SrvCall->BufferingManager);
6879 
6880     /* Set the flag, and get the requests */
6881     InitializeListHead(&SrvOpen->SrvOpenKeyList);
6882     SetFlag(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_REQUESTS_PURGED);
6883     RxGatherRequestsForSrvOpen(SrvCall, SrvOpen, &Discarded);
6884 
6885     RxReleaseBufferingManagerMutex(&SrvCall->BufferingManager);
6886 
6887     /* If there were discarded requests */
6888     if (!IsListEmpty(&Discarded))
6889     {
6890         /* And a pending buffering state change */
6891         if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING))
6892         {
6893             /* Clear the flag, and set the associated event - job done */
6894             RxAcquireSerializationMutex();
6895             ClearFlag(SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
6896             if (SrvOpen->Fcb->pBufferingStateChangeCompletedEvent != NULL)
6897             {
6898                 KeSetEvent(SrvOpen->Fcb->pBufferingStateChangeCompletedEvent, IO_NETWORK_INCREMENT, FALSE);
6899             }
6900             RxReleaseSerializationMutex();
6901         }
6902 
6903         /* Drop the discarded requests */
6904         RxpDiscardChangeBufferingStateRequests(&Discarded);
6905     }
6906 }
6907 
6908 /*
6909  * @implemented
6910  */
6911 VOID
6912 RxPurgeFcb(
6913     IN  PFCB Fcb)
6914 {
6915     PAGED_CODE();
6916 
6917     ASSERT(RxIsFcbAcquiredExclusive(Fcb));
6918 
6919     /* Reference our FCB so that it doesn't disappear */
6920     RxReferenceNetFcb(Fcb);
6921     /* Purge Cc if required */
6922     if (Fcb->OpenCount != 0)
6923     {
6924         RxPurgeFcbInSystemCache(Fcb, NULL, 0, TRUE, TRUE);
6925     }
6926 
6927     /* If it wasn't freed, release the lock */
6928     if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
6929     {
6930         RxReleaseFcb(NULL, Fcb);
6931     }
6932 }
6933 
6934 /*
6935  * @implemented
6936  */
6937 NTSTATUS
6938 RxPurgeFcbInSystemCache(
6939     IN PFCB Fcb,
6940     IN PLARGE_INTEGER FileOffset OPTIONAL,
6941     IN ULONG Length,
6942     IN BOOLEAN UninitializeCacheMaps,
6943     IN BOOLEAN FlushFile)
6944 {
6945     BOOLEAN Purged;
6946     NTSTATUS Status;
6947 
6948     PAGED_CODE();
6949 
6950     ASSERT(RxIsFcbAcquiredExclusive(Fcb));
6951 
6952     /* Try to flush first, if asked */
6953     if (FlushFile)
6954     {
6955         /* If flushing failed, just make some noise */
6956         Status = RxFlushFcbInSystemCache(Fcb, TRUE);
6957         if (!NT_SUCCESS(Status))
6958         {
6959             PVOID CallersAddress, CallersCaller;
6960 
6961             RtlGetCallersAddress(&CallersAddress, &CallersCaller);
6962             DPRINT1("Flush failed with status %lx for FCB %p\n", Status, Fcb);
6963             DPRINT1("Caller was %p %p\n", CallersAddress, CallersCaller);
6964         }
6965     }
6966 
6967     /* Deal with Cc for purge */
6968     Purged = CcPurgeCacheSection(&Fcb->NonPaged->SectionObjectPointers, FileOffset,
6969                                  Length, UninitializeCacheMaps);
6970     /* If purge failed, force section closing */
6971     if (!Purged)
6972     {
6973         MmFlushImageSection(&Fcb->NonPaged->SectionObjectPointers, MmFlushForWrite);
6974 
6975         RxReleaseFcb(NULL, Fcb);
6976         Purged = MmForceSectionClosed(&Fcb->NonPaged->SectionObjectPointers, TRUE);
6977         RxAcquireExclusiveFcb(NULL, Fcb);
6978     }
6979 
6980     /* Return appropriate status */
6981     Status = (Purged ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
6982     DPRINT("Purge for FCB %p returns %lx\n", Fcb, Status);
6983 
6984     return Status;
6985 }
6986 
6987 /*
6988  * @implemented
6989  */
6990 BOOLEAN
6991 RxPurgeFobx(
6992     PFOBX pFobx)
6993 {
6994     NTSTATUS Status;
6995     PFCB FcbToBePurged;
6996 
6997     PAGED_CODE();
6998 
6999     /* Get the associated FCB */
7000     FcbToBePurged = (PFCB)pFobx->pSrvOpen->pFcb;
7001     Status = RxAcquireExclusiveFcb(NULL, FcbToBePurged);
7002     ASSERT(Status == STATUS_SUCCESS);
7003 
7004     /* Purge it */
7005     Status = RxPurgeFcbInSystemCache(FcbToBePurged, NULL, 0, FALSE, TRUE);
7006     if (Status != STATUS_SUCCESS)
7007     {
7008         DPRINT1("Purge failed for %p (%p)\n", FcbToBePurged, pFobx);
7009         return FALSE;
7010     }
7011 
7012     /* And flush */
7013     if (!MmFlushImageSection(&FcbToBePurged->NonPaged->SectionObjectPointers, MmFlushForWrite))
7014     {
7015         DPRINT1("Image section flush failed for %p (%p)\n", FcbToBePurged, pFobx);
7016         return FALSE;
7017     }
7018 
7019     DPRINT("Purge OK for %p (%p)\n", FcbToBePurged, pFobx);
7020     return TRUE;
7021 }
7022 
7023 /*
7024  * @implemented
7025  */
7026 NTSTATUS
7027 RxPurgeFobxFromCache(
7028     PFOBX FobxToBePurged)
7029 {
7030     NTSTATUS Status;
7031     PFCB FcbToBePurged;
7032 
7033     PAGED_CODE();
7034 
7035     FcbToBePurged = (PFCB)FobxToBePurged->pSrvOpen->pFcb;
7036     ASSERT(FcbToBePurged != NULL);
7037 
7038     /* If we cannot have our FCB exclusively, give up */
7039     Status = RxAcquireExclusiveFcb(NULL, FcbToBePurged);
7040     if (Status != STATUS_SUCCESS)
7041     {
7042         RxDereferenceNetFobx(FobxToBePurged, LHS_LockNotHeld);
7043         return Status;
7044     }
7045 
7046     /* Don't let the FCB disappear */
7047     RxReferenceNetFcb(FcbToBePurged);
7048 
7049     /* If the SRV_OPEN was already closed, or if there are unclean FOBX, give up */
7050     if (BooleanFlagOn(FobxToBePurged->Flags, FOBX_FLAG_SRVOPEN_CLOSED) || FobxToBePurged->pSrvOpen->UncleanFobxCount != 0)
7051     {
7052         DPRINT("FCB purge skipped\n");
7053     }
7054     else
7055     {
7056         Status = RxPurgeFcbInSystemCache(FcbToBePurged, NULL, 0, FALSE, TRUE);
7057     }
7058 
7059     RxDereferenceNetFobx(FobxToBePurged, LHS_ExclusiveLockHeld);
7060     /* Drop our extra reference */
7061     if (!RxDereferenceAndFinalizeNetFcb(FcbToBePurged, NULL, FALSE, FALSE))
7062     {
7063         RxReleaseFcb(NULL, FcbToBePurged);
7064     }
7065 
7066     return Status;
7067 }
7068 
7069 /*
7070  * @implemented
7071  */
7072 NTSTATUS
7073 RxPurgeRelatedFobxs(
7074     PNET_ROOT NetRoot,
7075     PRX_CONTEXT RxContext,
7076     BOOLEAN AttemptFinalization,
7077     PFCB PurgingFcb)
7078 {
7079     PLIST_ENTRY Entry;
7080     ULONG SuccessfullPurge;
7081     PRDBSS_SCAVENGER Scavenger;
7082     PRDBSS_DEVICE_OBJECT RxDeviceObject;
7083     PPURGE_SYNCHRONIZATION_CONTEXT PurgeSyncCtx;
7084 
7085     PAGED_CODE();
7086 
7087     RxDeviceObject = RxContext->RxDeviceObject;
7088     Scavenger = RxDeviceObject->pRdbssScavenger;
7089     PurgeSyncCtx = &NetRoot->PurgeSyncronizationContext;
7090 
7091     RxAcquireScavengerMutex();
7092 
7093     /* If there's already a purge in progress */
7094     if (PurgeSyncCtx->PurgeInProgress)
7095     {
7096         /* Add our RX_CONTEXT to the current run */
7097         InsertTailList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion,
7098                        &RxContext->RxContextSerializationQLinks);
7099 
7100         /* And wait until it's done */
7101         RxReleaseScavengerMutex();
7102         RxWaitSync(RxContext);
7103         RxAcquireScavengerMutex();
7104     }
7105 
7106     /* Start the purge */
7107     PurgeSyncCtx->PurgeInProgress = TRUE;
7108 
7109     /* While the purge is still handling our NET_ROOT, do nothing but wait */
7110     while (Scavenger->CurrentNetRootForClosePendingProcessing == NetRoot)
7111     {
7112         RxReleaseScavengerMutex();
7113         KeWaitForSingleObject(&Scavenger->ClosePendingProcessingSyncEvent, Executive,
7114                               KernelMode, TRUE, NULL);
7115         RxAcquireScavengerMutex();
7116     }
7117 
7118     /* Now, for all the entries */
7119     SuccessfullPurge = 0;
7120     Entry = Scavenger->ClosePendingFobxsList.Flink;
7121     while (Entry != &Scavenger->ClosePendingFobxsList)
7122     {
7123         PFCB Fcb;
7124         PFOBX Fobx;
7125         BOOLEAN Success;
7126 
7127         Fobx = CONTAINING_RECORD(Entry, FOBX, ClosePendingList);
7128         DPRINT("Dealing with FOBX: %p\n", Fobx);
7129 
7130         Entry = Entry->Flink;
7131 
7132         /* If it's not matching our NET_ROOT, ignore */
7133         if (Fobx->pSrvOpen == NULL ||
7134             Fobx->pSrvOpen->pFcb == NULL ||
7135             ((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot == NULL ||
7136             (PNET_ROOT)((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot->pNetRoot != NetRoot)
7137         {
7138             continue;
7139         }
7140 
7141         /* Determine if it matches our FCB */
7142         Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
7143         if (PurgingFcb != NULL && NodeType(PurgingFcb) != RDBSS_NTC_STORAGE_TYPE_DIRECTORY &&
7144             PurgingFcb != Fcb)
7145         {
7146             NTSTATUS Status;
7147 
7148             MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb));
7149             if (Status == STATUS_SUCCESS)
7150             {
7151                 continue;
7152             }
7153         }
7154 
7155         /* Matching, we'll purge it */
7156         RemoveEntryList(&Fobx->ClosePendingList);
7157 
7158         /* Reference it so that it doesn't disappear */
7159         RxReferenceNetFobx(Fobx);
7160 
7161         RxReleaseScavengerMutex();
7162 
7163         /* And purge */
7164         Success = RxPurgeFobx(Fobx);
7165         if (Success)
7166         {
7167             ++SuccessfullPurge;
7168         }
7169 
7170         /* If we don't have to finalize it (or if we cannot acquire lock exclusively
7171          * Just normally dereference
7172          */
7173         if ((AttemptFinalization == DONT_ATTEMPT_FINALIZE_ON_PURGE) ||
7174             RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS)
7175         {
7176             RxDereferenceNetFobx(Fobx, LHS_LockNotHeld);
7177         }
7178         /* Otherwise, finalize */
7179         else
7180         {
7181             RxReferenceNetFcb(Fcb);
7182             RxDereferenceNetFobx(Fobx, LHS_ExclusiveLockHeld);
7183             if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
7184             {
7185                 RxReleaseFcb(NULL, Fcb);
7186             }
7187         }
7188 
7189         if (!Success)
7190         {
7191             DPRINT1("Failed purging %p (%p)\n", Fcb, Fobx);
7192         }
7193 
7194         RxAcquireScavengerMutex();
7195     }
7196 
7197     /* If no contexts left, purge is not running */
7198     if (IsListEmpty(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion))
7199     {
7200         PurgeSyncCtx->PurgeInProgress = FALSE;
7201     }
7202     /* Otherwise, notify a waiter it can start */
7203     else
7204     {
7205         PRX_CONTEXT Context;
7206 
7207         Entry = RemoveHeadList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion);
7208         Context = CONTAINING_RECORD(Entry, RX_CONTEXT, RxContextSerializationQLinks);
7209 
7210         RxSignalSynchronousWaiter(Context);
7211     }
7212 
7213     RxReleaseScavengerMutex();
7214 
7215     return (SuccessfullPurge > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
7216 }
7217 
7218 /*
7219  * @implemented
7220  */
7221 VOID
7222 RxpWorkerThreadDispatcher(
7223    IN PRX_WORK_QUEUE WorkQueue,
7224    IN PLARGE_INTEGER WaitInterval)
7225 {
7226     NTSTATUS Status;
7227     PVOID Parameter;
7228     PETHREAD CurrentThread;
7229     BOOLEAN KillThread, Dereference;
7230     PRX_WORK_QUEUE_ITEM WorkQueueItem;
7231     PWORKER_THREAD_ROUTINE WorkerRoutine;
7232 
7233     InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
7234 
7235     /* Reference ourselves */
7236     CurrentThread = PsGetCurrentThread();
7237     Status = ObReferenceObjectByPointer(CurrentThread, THREAD_ALL_ACCESS, *PsThreadType, KernelMode);
7238     ASSERT(NT_SUCCESS(Status));
7239 
7240     /* Infinite loop for worker */
7241     KillThread = FALSE;
7242     Dereference = FALSE;
7243     do
7244     {
7245         KIRQL OldIrql;
7246         PLIST_ENTRY ListEntry;
7247 
7248         /* Remove an entry from the work queue */
7249         ListEntry = KeRemoveQueue(&WorkQueue->Queue, KernelMode, WaitInterval);
7250         if ((ULONG_PTR)ListEntry != STATUS_TIMEOUT)
7251         {
7252             PRDBSS_DEVICE_OBJECT DeviceObject;
7253 
7254             WorkQueueItem = CONTAINING_RECORD(ListEntry, RX_WORK_QUEUE_ITEM, List);
7255 
7256             InterlockedIncrement(&WorkQueue->NumberOfWorkItemsDispatched);
7257             InterlockedDecrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
7258             InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
7259 
7260             /* Get the parameters, and null-them in the struct */
7261             WorkerRoutine = WorkQueueItem->WorkerRoutine;
7262             Parameter = WorkQueueItem->Parameter;
7263             DeviceObject = WorkQueueItem->pDeviceObject;
7264 
7265             WorkQueueItem->List.Flink = NULL;
7266             WorkQueueItem->WorkerRoutine = NULL;
7267             WorkQueueItem->Parameter = NULL;
7268             WorkQueueItem->pDeviceObject = NULL;
7269 
7270             /* Call the routine */
7271             DPRINT("Calling: %p(%p)\n", WorkerRoutine, Parameter);
7272             WorkerRoutine(Parameter);
7273 
7274             /* Are we going down now? */
7275             if (InterlockedDecrement(&DeviceObject->DispatcherContext.NumberOfWorkerThreads) == 0)
7276             {
7277                 PKEVENT TearDownEvent;
7278 
7279                 TearDownEvent = InterlockedExchangePointer((void * volatile*)&DeviceObject->DispatcherContext.pTearDownEvent, NULL);
7280                 if (TearDownEvent != NULL)
7281                 {
7282                     KeSetEvent(TearDownEvent, IO_NO_INCREMENT, FALSE);
7283                 }
7284             }
7285 
7286             InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
7287         }
7288 
7289         /* Shall we shutdown... */
7290         KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
7291         switch (WorkQueue->State)
7292         {
7293             /* Our queue is active, kill it if we have no more items to dispatch
7294              * and more threads than the required minimum
7295              */
7296             case RxWorkQueueActive:
7297                 if (WorkQueue->NumberOfWorkItemsToBeDispatched <= 0)
7298                 {
7299                     ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
7300                     if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
7301                     {
7302                         KillThread = TRUE;
7303                         Dereference = TRUE;
7304                         InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
7305                     }
7306 
7307                     if (KillThread)
7308                     {
7309                         InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
7310                     }
7311                 }
7312                 break;
7313 
7314             /* The queue is inactive: kill it we have more threads than the required minimum */
7315             case RxWorkQueueInactive:
7316                 ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
7317                 if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
7318                 {
7319                     KillThread = TRUE;
7320                     Dereference = TRUE;
7321                     InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
7322                 }
7323 
7324                 if (KillThread)
7325                 {
7326                     InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
7327                 }
7328                 break;
7329 
7330             /* Rundown in progress..., kill it for sure! */
7331             case RxWorkQueueRundownInProgress:
7332                 {
7333                     PRX_WORK_QUEUE_RUNDOWN_CONTEXT RundownContext;
7334 
7335                     ASSERT(WorkQueue->pRundownContext != NULL);
7336 
7337                     RundownContext = WorkQueue->pRundownContext;
7338                     RundownContext->ThreadPointers[RundownContext->NumberOfThreadsSpunDown++] = CurrentThread;
7339 
7340                     InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
7341                     KillThread = TRUE;
7342                     Dereference = FALSE;
7343 
7344                     if (WorkQueue->NumberOfActiveWorkerThreads == 0)
7345                     {
7346                         KeSetEvent(&RundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
7347                     }
7348 
7349                     InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
7350                 }
7351                 break;
7352 
7353             default:
7354                 break;
7355         }
7356         KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
7357     } while (!KillThread);
7358 
7359     DPRINT("Killed worker thread\n");
7360 
7361     /* Do we have to dereference ourselves? */
7362     if (Dereference)
7363     {
7364         ObDereferenceObject(CurrentThread);
7365     }
7366 
7367     /* Dump last executed routine */
7368     if (DumpDispatchRoutine)
7369     {
7370         DPRINT("Dispatch routine %p(%p) taken from %p\n", WorkerRoutine, Parameter, WorkQueueItem);
7371     }
7372 
7373     PsTerminateSystemThread(STATUS_SUCCESS);
7374 }
7375 
7376 VOID
7377 RxReference(
7378     IN OUT PVOID Instance)
7379 {
7380     NODE_TYPE_CODE NodeType;
7381     PNODE_TYPE_AND_SIZE Node;
7382 
7383     PAGED_CODE();
7384 
7385     RxAcquireScavengerMutex();
7386 
7387     /* We can only reference a few structs */
7388     NodeType = NodeType(Instance) & ~RX_SCAVENGER_MASK;
7389     ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
7390            (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
7391            (NodeType == RDBSS_NTC_FOBX));
7392 
7393     Node = (PNODE_TYPE_AND_SIZE)Instance;
7394     InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
7395 
7396     /* Trace refcount if asked */
7397     switch (NodeType)
7398     {
7399         case RDBSS_NTC_SRVCALL:
7400             PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
7401             break;
7402 
7403         case RDBSS_NTC_NETROOT:
7404             PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
7405             break;
7406 
7407         case RDBSS_NTC_V_NETROOT:
7408             PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
7409             break;
7410 
7411         case RDBSS_NTC_SRVOPEN:
7412             PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
7413             break;
7414 
7415         case RDBSS_NTC_FOBX:
7416             PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
7417             break;
7418 
7419         default:
7420             ASSERT(FALSE);
7421             break;
7422     }
7423 
7424     RxpUndoScavengerFinalizationMarking(Instance);
7425     RxReleaseScavengerMutex();
7426 }
7427 
7428 /*
7429  * @implemented
7430  */
7431 VOID
7432 NTAPI
7433 RxReinitializeContext(
7434    IN OUT PRX_CONTEXT RxContext)
7435 {
7436     PIRP Irp;
7437     PRDBSS_DEVICE_OBJECT RxDeviceObject;
7438     ULONG InitialContextFlags, SavedFlags;
7439 
7440     PAGED_CODE();
7441 
7442     /* Backup a few flags */
7443     Irp = RxContext->CurrentIrp;
7444     RxDeviceObject = RxContext->RxDeviceObject;
7445     SavedFlags = RxContext->Flags & RX_CONTEXT_PRESERVED_FLAGS;
7446     InitialContextFlags = RxContext->Flags & RX_CONTEXT_INITIALIZATION_FLAGS;
7447 
7448     /* Reset our context */
7449     RxPrepareContextForReuse(RxContext);
7450 
7451     /* Zero everything */
7452     RtlZeroMemory(&RxContext->MajorFunction, sizeof(RX_CONTEXT) - FIELD_OFFSET(RX_CONTEXT, MajorFunction));
7453 
7454     /* Restore saved flags */
7455     RxContext->Flags = SavedFlags;
7456     /* And reinit the context */
7457     RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, RxContext);
7458 }
7459 
7460 /*
7461  * @implemented
7462  */
7463 VOID
7464 NTAPI
7465 RxReleaseFcbFromLazyWrite(
7466     PVOID Context)
7467 {
7468     PFCB Fcb;
7469 
7470     PAGED_CODE();
7471 
7472     Fcb = Context;
7473     /* The received context is a FCB */
7474     ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
7475     ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
7476 
7477     /* Lazy writer is releasing lock, so forget about it */
7478     Fcb->Specific.Fcb.LazyWriteThread = NULL;
7479 
7480     /* If we were top level IRP, unwind */
7481     if (RxGetTopIrpIfRdbssIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
7482     {
7483         RxUnwindTopLevelIrp(NULL);
7484     }
7485 
7486     /* And finally, release the lock */
7487     Fcb->PagingIoResourceFile = NULL;
7488     Fcb->PagingIoResourceLine = 0;
7489     ExReleaseResourceLite(Fcb->Header.PagingIoResource);
7490 }
7491 
7492 /*
7493  * @implemented
7494  */
7495 VOID
7496 NTAPI
7497 RxReleaseFcbFromReadAhead(
7498     PVOID Context)
7499 {
7500     PFCB Fcb;
7501 
7502     PAGED_CODE();
7503 
7504     Fcb = Context;
7505     /* The received context is a FCB */
7506     ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
7507     ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
7508 
7509     /* Top Level IRP is CC */
7510     ASSERT(RxGetTopIrpIfRdbssIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
7511     RxUnwindTopLevelIrp(NULL);
7512 
7513     ExReleaseResourceLite(Fcb->Header.Resource);
7514 }
7515 
7516 VOID
7517 NTAPI
7518 RxReleaseFileForNtCreateSection(
7519     PFILE_OBJECT FileObject)
7520 {
7521     UNIMPLEMENTED;
7522 }
7523 
7524 NTSTATUS
7525 NTAPI
7526 RxReleaseForCcFlush(
7527     PFILE_OBJECT FileObject,
7528     PDEVICE_OBJECT DeviceObject)
7529 {
7530     UNIMPLEMENTED;
7531     return STATUS_NOT_IMPLEMENTED;
7532 }
7533 
7534 /*
7535  * @implemented
7536  */
7537 VOID
7538 RxRemoveNameNetFcb(
7539     OUT PFCB ThisFcb)
7540 {
7541     PNET_ROOT NetRoot;
7542 
7543     PAGED_CODE();
7544 
7545     ASSERT(NodeTypeIsFcb(ThisFcb));
7546 
7547     /* Just remove the entry from the FCB_TABLE */
7548     NetRoot = (PNET_ROOT)ThisFcb->VNetRoot->pNetRoot;
7549     ASSERT(RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
7550     ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
7551 
7552 #ifdef __REACTOS__
7553     if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED))
7554     {
7555 #endif
7556         RxFcbTableRemoveFcb(&NetRoot->FcbTable, ThisFcb);
7557         DPRINT("FCB (%p) %wZ removed\n", ThisFcb, &ThisFcb->FcbTableEntry.Path);
7558         /* Mark, so that we don't try to do it twice */
7559         SetFlag(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED);
7560 #ifdef __REACTOS__
7561     }
7562 #endif
7563 }
7564 
7565 /*
7566  * @implemented
7567  */
7568 VOID
7569 RxRemovePrefixTableEntry(
7570     IN OUT PRX_PREFIX_TABLE ThisTable,
7571     IN OUT PRX_PREFIX_ENTRY Entry)
7572 {
7573     PAGED_CODE();
7574 
7575     ASSERT(NodeType(Entry) == RDBSS_NTC_PREFIX_ENTRY);
7576     ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
7577 
7578     /* Check whether we're asked to remove null entry */
7579     if (Entry->Prefix.Length == 0)
7580     {
7581         ThisTable->TableEntryForNull = NULL;
7582     }
7583     else
7584     {
7585         RemoveEntryList(&Entry->HashLinks);
7586     }
7587 
7588     Entry->ContainingRecord = NULL;
7589 
7590     /* Also remove it from global list */
7591     RemoveEntryList(&Entry->MemberQLinks);
7592 
7593     ++ThisTable->Version;
7594 }
7595 
7596 /*
7597  * @implemented
7598  */
7599 VOID
7600 RxRemoveVirtualNetRootFromNetRoot(
7601     PNET_ROOT NetRoot,
7602     PV_NET_ROOT VNetRoot)
7603 {
7604     PRX_PREFIX_TABLE PrefixTable;
7605 
7606     PAGED_CODE();
7607 
7608     PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
7609     ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
7610 
7611     /* Remove the VNetRoot from the list in the NetRoot */
7612     --NetRoot->NumberOfVirtualNetRoots;
7613     RemoveEntryList(&VNetRoot->NetRootListEntry);
7614 
7615     /* Fix the NetRoot if we were the default VNetRoot */
7616     if (NetRoot->DefaultVNetRoot == VNetRoot)
7617     {
7618         /* Put the first one available */
7619         if (!IsListEmpty(&NetRoot->VirtualNetRoots))
7620         {
7621             NetRoot->DefaultVNetRoot = CONTAINING_RECORD(NetRoot->VirtualNetRoots.Flink, V_NET_ROOT, NetRootListEntry);
7622         }
7623         /* Otherwise, none */
7624         else
7625         {
7626             NetRoot->DefaultVNetRoot = NULL;
7627         }
7628     }
7629 
7630     /* If there are still other VNetRoot available, we're done */
7631     if (!IsListEmpty(&NetRoot->VirtualNetRoots))
7632     {
7633         return;
7634     }
7635 
7636     /* Otherwise, initiate NetRoot finalization */
7637     if (!BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED))
7638     {
7639         RxRemovePrefixTableEntry(PrefixTable, &NetRoot->PrefixEntry);
7640         SetFlag(NetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED);
7641     }
7642 
7643     /* Notify mini-rdr */
7644     if (NetRoot->pSrvCall != NULL && NetRoot->pSrvCall->RxDeviceObject != NULL)
7645     {
7646         NTSTATUS Status;
7647 
7648         MINIRDR_CALL_THROUGH(Status, NetRoot->pSrvCall->RxDeviceObject->Dispatch,
7649                              MRxFinalizeNetRoot, ((PMRX_NET_ROOT)NetRoot, FALSE));
7650         (void)Status;
7651     }
7652 }
7653 
7654 VOID
7655 RxResumeBlockedOperations_ALL(
7656     IN OUT PRX_CONTEXT RxContext)
7657 {
7658     LIST_ENTRY BlockedOps;
7659 
7660     PAGED_CODE();
7661 
7662     /* Get the blocked operations */
7663     RxTransferListWithMutex(&BlockedOps, &RxContext->BlockedOperations, RxContext->BlockedOpsMutex);
7664 
7665     if (!IsListEmpty(&BlockedOps))
7666     {
7667         UNIMPLEMENTED;
7668     }
7669 }
7670 
7671 VOID
7672 NTAPI
7673 RxResumeBlockedOperations_Serially(
7674     IN OUT PRX_CONTEXT RxContext,
7675     IN OUT PLIST_ENTRY BlockingIoQ)
7676 {
7677     PAGED_CODE();
7678 
7679     RxAcquireSerializationMutex();
7680 
7681     /* This can only happen on pipes */
7682     if (!BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION))
7683     {
7684         RxReleaseSerializationMutex();
7685         return;
7686     }
7687 
7688     UNIMPLEMENTED;
7689 
7690     RxReleaseSerializationMutex();
7691 }
7692 
7693 /*
7694  * @implemented
7695  */
7696 VOID
7697 RxSetFileSizeWithLock(
7698     IN OUT PFCB Fcb,
7699     IN PLONGLONG FileSize)
7700 {
7701     PAGED_CODE();
7702 
7703     /* Set attribute and increase version */
7704     Fcb->Header.FileSize.QuadPart = *FileSize;
7705     ++Fcb->ulFileSizeVersion;
7706 }
7707 
7708 /*
7709  * @implemented
7710  */
7711 VOID
7712 RxScavengeFobxsForNetRoot(
7713     PNET_ROOT NetRoot,
7714     PFCB PurgingFcb,
7715     BOOLEAN SynchronizeWithScavenger)
7716 {
7717     PRDBSS_SCAVENGER Scavenger;
7718     PRDBSS_DEVICE_OBJECT RxDeviceObject;
7719 
7720     PAGED_CODE();
7721 
7722     RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject;
7723     Scavenger = RxDeviceObject->pRdbssScavenger;
7724 
7725     /* Wait for the scavenger, if asked to */
7726     if (SynchronizeWithScavenger)
7727     {
7728         KeWaitForSingleObject(&Scavenger->ScavengeEvent, Executive, KernelMode, FALSE, NULL);
7729     }
7730 
7731     RxAcquireScavengerMutex();
7732 
7733     /* If there's nothing left to do... */
7734     if (Scavenger->FobxsToBeFinalized <= 0)
7735     {
7736         RxReleaseScavengerMutex();
7737     }
7738     else
7739     {
7740         PLIST_ENTRY Entry;
7741         LIST_ENTRY FobxToScavenge;
7742 
7743         InitializeListHead(&FobxToScavenge);
7744 
7745         /* Browse all the FOBXs to finalize */
7746         Entry = Scavenger->FobxFinalizationList.Flink;
7747         while (Entry != &Scavenger->FobxFinalizationList)
7748         {
7749             PFOBX Fobx;
7750 
7751             Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList);
7752             Entry = Entry->Flink;
7753 
7754             if (Fobx->SrvOpen != NULL)
7755             {
7756                 PFCB Fcb;
7757 
7758                 Fcb = (PFCB)Fobx->SrvOpen->pFcb;
7759 
7760                 /* If it matches our NET_ROOT */
7761                 if ((PNET_ROOT)Fcb->pNetRoot == NetRoot)
7762                 {
7763                     NTSTATUS Status;
7764 
7765                     /* Check whether it matches our FCB */
7766                     Status = STATUS_MORE_PROCESSING_REQUIRED;
7767                     if (PurgingFcb != NULL && PurgingFcb != Fcb)
7768                     {
7769                         MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb));
7770                     }
7771 
7772                     /* If so, add it to the list of the FOBXs to scavenge */
7773                     if (Status != STATUS_SUCCESS)
7774                     {
7775                         RxReferenceNetFobx(Fobx);
7776                         ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX);
7777 
7778                         RemoveEntryList(&Fobx->ScavengerFinalizationList);
7779                         InsertTailList(&FobxToScavenge, &Fobx->ScavengerFinalizationList);
7780                     }
7781                 }
7782             }
7783         }
7784 
7785         RxReleaseScavengerMutex();
7786 
7787         /* Now, scavenge all the extracted FOBX */
7788         RxpScavengeFobxs(Scavenger, &FobxToScavenge);
7789     }
7790 
7791     if (SynchronizeWithScavenger)
7792     {
7793         KeSetEvent(&Scavenger->ScavengeEvent, IO_NO_INCREMENT, FALSE);
7794     }
7795 }
7796 
7797 /*
7798  * @implemented
7799  */
7800 BOOLEAN
7801 RxScavengeRelatedFobxs(
7802     PFCB Fcb)
7803 {
7804     PFOBX Fobx;
7805     LIST_ENTRY LocalList;
7806     PLIST_ENTRY NextEntry;
7807     PRDBSS_SCAVENGER Scavenger;
7808 
7809     PAGED_CODE();
7810 
7811     /* First of all, check whether there are FOBX to scavenge */
7812     Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
7813     RxAcquireScavengerMutex();
7814     if (Scavenger->FobxsToBeFinalized <= 0)
7815     {
7816         RxReleaseScavengerMutex();
7817         return FALSE;
7818     }
7819 
7820     /* Initialize our local list which will hold all the FOBX to scavenge so
7821      * that we don't acquire the scavenger mutex too long
7822      */
7823     InitializeListHead(&LocalList);
7824 
7825     /* Technically, that condition should all be true... */
7826     if (!IsListEmpty(&Scavenger->FobxFinalizationList))
7827     {
7828         PLIST_ENTRY NextEntry, LastEntry;
7829 
7830         /* Browse all the FCBs to find the matching ones */
7831         NextEntry = Scavenger->FobxFinalizationList.Flink;
7832         LastEntry = &Scavenger->FobxFinalizationList;
7833         while (NextEntry != LastEntry)
7834         {
7835             Fobx = CONTAINING_RECORD(NextEntry, FOBX, ScavengerFinalizationList);
7836             NextEntry = NextEntry->Flink;
7837             /* Matching our FCB? Let's finalize it */
7838             if (Fobx->pSrvOpen != NULL && Fobx->pSrvOpen->pFcb == RX_GET_MRX_FCB(Fcb))
7839             {
7840                 RxpUndoScavengerFinalizationMarking(Fobx);
7841                 ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX);
7842                 InsertTailList(&LocalList, &Fobx->ScavengerFinalizationList);
7843             }
7844         }
7845     }
7846 
7847     RxReleaseScavengerMutex();
7848 
7849     /* Nothing to scavenge? Quit */
7850     if (IsListEmpty(&LocalList))
7851     {
7852         return FALSE;
7853     }
7854 
7855     /* Now, finalize all the extracted FOBX */
7856     while (!IsListEmpty(&LocalList))
7857     {
7858         NextEntry = RemoveHeadList(&LocalList);
7859         Fobx = CONTAINING_RECORD(NextEntry, FOBX, ScavengerFinalizationList);
7860         RxFinalizeNetFobx(Fobx, TRUE, TRUE);
7861     }
7862 
7863     return TRUE;
7864 }
7865 
7866 VOID
7867 RxScavengerFinalizeEntries(
7868     PRDBSS_DEVICE_OBJECT DeviceObject)
7869 {
7870     UNIMPLEMENTED;
7871 }
7872 
7873 /*
7874  * @implemented
7875  */
7876 VOID
7877 NTAPI
7878 RxScavengerTimerRoutine(
7879     PVOID Context)
7880 {
7881     BOOLEAN Requeue;
7882     PRDBSS_DEVICE_OBJECT DeviceObject;
7883     PRDBSS_SCAVENGER Scavenger;
7884 
7885     PAGED_CODE();
7886 
7887     DeviceObject = Context;
7888     Scavenger = DeviceObject->pRdbssScavenger;
7889 
7890     Requeue = FALSE;
7891     RxAcquireScavengerMutex();
7892     /* If the scavenger was dormant, wake it up! */
7893     if (Scavenger->State == RDBSS_SCAVENGER_DORMANT)
7894     {
7895         /* Done */
7896         Scavenger->State = RDBSS_SCAVENGER_ACTIVE;
7897         KeClearEvent(&Scavenger->ScavengeEvent);
7898 
7899         /* Scavenger the entries */
7900         RxReleaseScavengerMutex();
7901         RxScavengerFinalizeEntries(DeviceObject);
7902         RxAcquireScavengerMutex();
7903 
7904         /* If we're still active (race) */
7905         if (Scavenger->State == RDBSS_SCAVENGER_ACTIVE)
7906         {
7907             /* If there are new entries to scavenge, stay dormant and requeue a run */
7908             if (Scavenger->NumberOfDormantFiles + Scavenger->SrvCallsToBeFinalized +
7909                 Scavenger->NetRootsToBeFinalized + Scavenger->VNetRootsToBeFinalized +
7910                 Scavenger->FcbsToBeFinalized + Scavenger->SrvOpensToBeFinalized +
7911                 Scavenger->FobxsToBeFinalized != 0)
7912             {
7913                 Requeue = TRUE;
7914                 Scavenger->State = RDBSS_SCAVENGER_DORMANT;
7915             }
7916             /* Otherwise, we're inactive again */
7917             else
7918             {
7919                 Scavenger->State = RDBSS_SCAVENGER_INACTIVE;
7920             }
7921         }
7922 
7923         RxReleaseScavengerMutex();
7924 
7925         /* Requeue an execution */
7926         if (Requeue)
7927         {
7928             RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem,
7929                                       RxScavengerTimerRoutine, DeviceObject, Scavenger->TimeLimit);
7930         }
7931     }
7932     else
7933     {
7934         RxReleaseScavengerMutex();
7935     }
7936 
7937     KeSetEvent(&Scavenger->ScavengeEvent, IO_NO_INCREMENT, FALSE);
7938 }
7939 
7940 BOOLEAN
7941 RxScavengeVNetRoots(
7942     PRDBSS_DEVICE_OBJECT RxDeviceObject)
7943 {
7944     UNIMPLEMENTED;
7945     return FALSE;
7946 }
7947 
7948 /*
7949  * @implemented
7950  */
7951 VOID
7952 NTAPI
7953 RxSpinUpRequestsDispatcher(
7954     PVOID Dispatcher)
7955 {
7956     NTSTATUS Status;
7957     PRX_DISPATCHER RxDispatcher;
7958 
7959     Status = ObReferenceObjectByPointer(PsGetCurrentThread(), THREAD_ALL_ACCESS, *PsThreadType, KernelMode);
7960     if (!NT_SUCCESS(Status))
7961     {
7962         PsTerminateSystemThread(STATUS_SUCCESS);
7963     }
7964 
7965     RxDispatcher = Dispatcher;
7966 
7967     do
7968     {
7969         KIRQL OldIrql;
7970         PLIST_ENTRY ListEntry;
7971 
7972         Status = KeWaitForSingleObject(&RxDispatcher->SpinUpRequestsEvent, Executive,
7973                                        KernelMode, FALSE, &RxSpinUpDispatcherWaitInterval);
7974         ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_TIMEOUT));
7975 
7976         KeAcquireSpinLock(&RxDispatcher->SpinUpRequestsLock, &OldIrql);
7977         if (!IsListEmpty(&RxDispatcher->SpinUpRequests))
7978         {
7979             ListEntry = RemoveHeadList(&RxDispatcher->SpinUpRequests);
7980         }
7981         else
7982         {
7983             ListEntry = &RxDispatcher->SpinUpRequests;
7984         }
7985         KeClearEvent(&RxDispatcher->SpinUpRequestsEvent);
7986         KeReleaseSpinLock(&RxDispatcher->SpinUpRequestsLock, OldIrql);
7987 
7988         while (ListEntry != &RxDispatcher->SpinUpRequests)
7989         {
7990             PWORK_QUEUE_ITEM WorkItem;
7991             PRX_WORK_QUEUE WorkQueue;
7992 
7993             WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List);
7994             WorkQueue = WorkItem->Parameter;
7995 
7996             InterlockedDecrement(&WorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
7997 
7998             DPRINT("Workqueue: calling %p(%p)\n", WorkItem->WorkerRoutine, WorkItem->Parameter);
7999             WorkItem->WorkerRoutine(WorkItem->Parameter);
8000         }
8001     } while (RxDispatcher->State == RxDispatcherActive);
8002 
8003     KeSetEvent(&RxDispatcher->SpinUpRequestsTearDownEvent, IO_NO_INCREMENT, FALSE);
8004     PsTerminateSystemThread(STATUS_SUCCESS);
8005 }
8006 
8007 /*
8008  * @implemented
8009  */
8010 NTSTATUS
8011 RxSpinUpWorkerThread(
8012    PRX_WORK_QUEUE WorkQueue,
8013    PRX_WORKERTHREAD_ROUTINE Routine,
8014    PVOID Parameter)
8015 {
8016     KIRQL OldIrql;
8017     NTSTATUS Status;
8018     HANDLE ThreadHandle;
8019 
8020     PAGED_CODE();
8021 
8022     /* If work queue is inactive, that cannot work */
8023     KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
8024     if (WorkQueue->State != RxWorkQueueActive)
8025     {
8026         Status = STATUS_UNSUCCESSFUL;
8027         DPRINT("Workqueue not active! WorkQ: %p, State: %d, Active: %d\n", WorkQueue, WorkQueue->State, WorkQueue->NumberOfActiveWorkerThreads);
8028     }
8029     else
8030     {
8031         ++WorkQueue->NumberOfActiveWorkerThreads;
8032         Status = STATUS_SUCCESS;
8033     }
8034     KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
8035 
8036     /* Quit on failure */
8037     if (!NT_SUCCESS(Status))
8038     {
8039         return Status;
8040     }
8041 
8042     /* Spin up the worker thread */
8043     Status = PsCreateSystemThread(&ThreadHandle, PROCESS_ALL_ACCESS, NULL, NULL, NULL, Routine, Parameter);
8044     if (NT_SUCCESS(Status))
8045     {
8046         ZwClose(ThreadHandle);
8047         return Status;
8048     }
8049     /* Read well: we reached that point because it failed! */
8050     DPRINT("WorkQ: %p, Status: %lx\n", WorkQueue, Status);
8051 
8052     KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
8053     --WorkQueue->NumberOfActiveWorkerThreads;
8054     ++WorkQueue->NumberOfFailedSpinUpRequests;
8055 
8056     /* Rundown, no more active threads, set the event! */
8057     if (WorkQueue->NumberOfActiveWorkerThreads == 0 &&
8058         WorkQueue->State == RxWorkQueueRundownInProgress)
8059     {
8060         KeSetEvent(&WorkQueue->pRundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
8061     }
8062 
8063     DPRINT("Workqueue not active! WorkQ: %p, State: %d, Active: %d\n", WorkQueue, WorkQueue->State, WorkQueue->NumberOfActiveWorkerThreads);
8064 
8065     KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
8066 
8067     return Status;
8068 }
8069 
8070 VOID
8071 RxSpinUpWorkerThreads(
8072    PRX_WORK_QUEUE WorkQueue)
8073 {
8074     UNIMPLEMENTED;
8075 }
8076 
8077 VOID
8078 RxSynchronizeWithScavenger(
8079     IN PRX_CONTEXT RxContext)
8080 {
8081     UNIMPLEMENTED;
8082 }
8083 
8084 /*
8085  * @implemented
8086  */
8087 ULONG
8088 RxTableComputeHashValue(
8089     IN PUNICODE_STRING Name)
8090 {
8091     ULONG Hash;
8092     SHORT Loops[8];
8093     USHORT MaxChar, i;
8094 
8095     PAGED_CODE();
8096 
8097     MaxChar = Name->Length / sizeof(WCHAR);
8098 
8099     Loops[0] = 1;
8100     Loops[1] = MaxChar - 1;
8101     Loops[2] = MaxChar - 2;
8102     Loops[3] = MaxChar - 3;
8103     Loops[4] = MaxChar - 4;
8104     Loops[5] = MaxChar / 4;
8105     Loops[6] = 2 * MaxChar / 4;
8106     Loops[7] = 3 * MaxChar / 4;
8107 
8108     Hash = 0;
8109     for (i = 0; i < 8; ++i)
8110     {
8111         SHORT Idx;
8112 
8113         Idx = Loops[i];
8114         if (Idx >= 0 && Idx < MaxChar)
8115         {
8116             Hash = RtlUpcaseUnicodeChar(Name->Buffer[Idx]) + 8 * Hash;
8117         }
8118     }
8119 
8120     return Hash;
8121 }
8122 
8123 /*
8124  * @implemented
8125  */
8126 ULONG
8127 RxTableComputePathHashValue(
8128     IN PUNICODE_STRING Name)
8129 {
8130     ULONG Hash;
8131     SHORT Loops[8];
8132     USHORT MaxChar, i;
8133 
8134     PAGED_CODE();
8135 
8136     MaxChar = Name->Length / sizeof(WCHAR);
8137 
8138     Loops[0] = 1;
8139     Loops[1] = MaxChar - 1;
8140     Loops[2] = MaxChar - 2;
8141     Loops[3] = MaxChar - 3;
8142     Loops[4] = MaxChar - 4;
8143     Loops[5] = MaxChar / 4;
8144     Loops[6] = 2 * MaxChar / 4;
8145     Loops[7] = 3 * MaxChar / 4;
8146 
8147     Hash = 0;
8148     for (i = 0; i < 8; ++i)
8149     {
8150         SHORT Idx;
8151 
8152         Idx = Loops[i];
8153         if (Idx >= 0 && Idx < MaxChar)
8154         {
8155             Hash = RtlUpcaseUnicodeChar(Name->Buffer[Idx]) + 8 * Hash;
8156         }
8157     }
8158 
8159     return Hash;
8160 }
8161 
8162 /*
8163  * @implemented
8164  */
8165 PVOID
8166 RxTableLookupName(
8167     IN PRX_PREFIX_TABLE ThisTable,
8168     IN PUNICODE_STRING Name,
8169     OUT PUNICODE_STRING RemainingName,
8170     IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
8171 {
8172     PVOID Container;
8173     USHORT i, MaxChar;
8174     PRX_PREFIX_ENTRY Entry;
8175     RX_CONNECTION_ID NullId;
8176     UNICODE_STRING LookupString;
8177 
8178     PAGED_CODE();
8179 
8180     /* If caller didn't provide a connection ID, setup one */
8181     if (ThisTable->IsNetNameTable && RxConnectionId == NULL)
8182     {
8183         NullId.Luid.LowPart = 0;
8184         NullId.Luid.HighPart = 0;
8185         RxConnectionId = &NullId;
8186     }
8187 
8188     /* Validate name */
8189     ASSERT(Name->Buffer[0] == OBJ_NAME_PATH_SEPARATOR);
8190 
8191     Entry = NULL;
8192     Container = NULL;
8193     LookupString.Buffer = Name->Buffer;
8194     MaxChar = Name->Length / sizeof(WCHAR);
8195     /* We'll perform the lookup, path component after another */
8196     for (i = 1; i < MaxChar; ++i)
8197     {
8198         ULONG Hash;
8199         PRX_PREFIX_ENTRY CurEntry;
8200 
8201         /* Don't cut in the middle of a path element */
8202         if (Name->Buffer[i] != OBJ_NAME_PATH_SEPARATOR && Name->Buffer[i] != ':')
8203         {
8204             continue;
8205         }
8206 
8207         /* Perform lookup in the table */
8208         LookupString.Length = i * sizeof(WCHAR);
8209         Hash = RxTableComputeHashValue(&LookupString);
8210         CurEntry = RxTableLookupName_ExactLengthMatch(ThisTable, &LookupString, Hash, RxConnectionId);
8211 #if DBG
8212         ++ThisTable->Lookups;
8213 #endif
8214         /* Entry not found, move to the next component */
8215         if (CurEntry == NULL)
8216         {
8217 #if DBG
8218             ++ThisTable->FailedLookups;
8219 #endif
8220             continue;
8221         }
8222 
8223         Entry = CurEntry;
8224         ASSERT(Entry->ContainingRecord != NULL);
8225         Container = Entry->ContainingRecord;
8226 
8227         /* If we have a NET_ROOT, let's return a V_NET_ROOT */
8228         if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_NETROOT)
8229         {
8230             PNET_ROOT NetRoot;
8231 
8232             NetRoot = (PNET_ROOT)Entry->ContainingRecord;
8233             /* If there's a default one, perfect, that's a match */
8234             if (NetRoot->DefaultVNetRoot != NULL)
8235             {
8236                 Container = NetRoot->DefaultVNetRoot;
8237             }
8238             /* If none (that shouldn't happen!), try to find one */
8239             else
8240             {
8241                 /* Use the first one in the list */
8242                 if (!IsListEmpty(&NetRoot->VirtualNetRoots))
8243                 {
8244                     Container = CONTAINING_RECORD(NetRoot->VirtualNetRoots.Flink, V_NET_ROOT, NetRootListEntry);
8245                 }
8246                 /* Really, really, shouldn't happen */
8247                 else
8248                 {
8249                     ASSERT(FALSE);
8250                     Entry = NULL;
8251                     Container = NULL;
8252                 }
8253             }
8254 
8255             break;
8256         }
8257         else if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_V_NETROOT)
8258         {
8259             break;
8260         }
8261         else
8262         {
8263             ASSERT((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_SRVCALL);
8264         }
8265     }
8266 
8267     /* Entry was found */
8268     if (Entry != NULL)
8269     {
8270         DPRINT("Found\n");
8271 
8272         ASSERT(Name->Length >= Entry->Prefix.Length);
8273 
8274         /* Setup remaining name */
8275         RemainingName->Buffer = Add2Ptr(Name->Buffer, Entry->Prefix.Length);
8276         RemainingName->Length = Name->Length - Entry->Prefix.Length;
8277         RemainingName->MaximumLength = Name->Length - Entry->Prefix.Length;
8278     }
8279     else
8280     {
8281         /* Otherwise, that's the whole name */
8282         RemainingName = Name;
8283     }
8284 
8285     return Container;
8286 }
8287 
8288 /*
8289  * @implemented
8290  */
8291 PRX_PREFIX_ENTRY
8292 RxTableLookupName_ExactLengthMatch(
8293     IN PRX_PREFIX_TABLE ThisTable,
8294     IN PUNICODE_STRING  Name,
8295     IN ULONG HashValue,
8296     IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
8297 {
8298     PLIST_ENTRY ListEntry, HashBucket;
8299 
8300     PAGED_CODE();
8301 
8302     ASSERT(RxConnectionId != NULL);
8303 
8304     /* Select the right bucket */
8305     HashBucket = HASH_BUCKET(ThisTable, HashValue);
8306     DPRINT("Looking in bucket: %p for %x\n", HashBucket, HashValue);
8307     /* If bucket is empty, no match */
8308     if (IsListEmpty(HashBucket))
8309     {
8310         return NULL;
8311     }
8312 
8313     /* Browse all the entries in the bucket */
8314     for (ListEntry = HashBucket->Flink;
8315          ListEntry != HashBucket;
8316          ListEntry = ListEntry->Flink)
8317     {
8318         PVOID Container;
8319         PRX_PREFIX_ENTRY Entry;
8320         BOOLEAN CaseInsensitive;
8321         PUNICODE_STRING CmpName, CmpPrefix;
8322         UNICODE_STRING InsensitiveName, InsensitivePrefix;
8323 
8324         Entry = CONTAINING_RECORD(ListEntry, RX_PREFIX_ENTRY, HashLinks);
8325 #if DBG
8326         ++ThisTable->Considers;
8327 #endif
8328         ASSERT(HashBucket == HASH_BUCKET(ThisTable, Entry->SavedHashValue));
8329 
8330         Container = Entry->ContainingRecord;
8331         ASSERT(Container != NULL);
8332 
8333         /* Not the same hash, not the same length, move on */
8334         if (Entry->SavedHashValue != HashValue || Entry->Prefix.Length != Name->Length)
8335         {
8336             continue;
8337         }
8338 
8339 #if DBG
8340         ++ThisTable->Compares;
8341 #endif
8342         /* If we have to perform a case insensitive compare on a portion... */
8343         if (Entry->CaseInsensitiveLength != 0)
8344         {
8345             ASSERT(Entry->CaseInsensitiveLength <= Name->Length);
8346 
8347             /* Perform the case insensitive check on the asked length */
8348             InsensitiveName.Buffer = Name->Buffer;
8349             InsensitivePrefix.Buffer = Entry->Prefix.Buffer;
8350             InsensitiveName.Length = Entry->CaseInsensitiveLength;
8351             InsensitivePrefix.Length = Entry->CaseInsensitiveLength;
8352             /* No match, move to the next entry */
8353             if (!RtlEqualUnicodeString(&InsensitiveName, &InsensitivePrefix, TRUE))
8354             {
8355                 continue;
8356             }
8357 
8358             /* Was the case insensitive covering the whole name? */
8359             if (Name->Length == Entry->CaseInsensitiveLength)
8360             {
8361                 /* If connection ID also matches, that a complete match! */
8362                 if (!ThisTable->IsNetNameTable || RxEqualConnectionId(RxConnectionId, &Entry->ConnectionId))
8363                 {
8364                     return Entry;
8365                 }
8366             }
8367 
8368             /* Otherwise, we have to continue with the sensitive match.... */
8369             InsensitiveName.Buffer = Add2Ptr(InsensitiveName.Buffer, Entry->CaseInsensitiveLength);
8370             InsensitivePrefix.Buffer = Add2Ptr(InsensitivePrefix.Buffer, Entry->CaseInsensitiveLength);
8371             InsensitiveName.Length = Name->Length - Entry->CaseInsensitiveLength;
8372             InsensitivePrefix.Length = Entry->Prefix.Length - Entry->CaseInsensitiveLength;
8373 
8374             CmpName = &InsensitiveName;
8375             CmpPrefix = &InsensitivePrefix;
8376             CaseInsensitive = FALSE;
8377         }
8378         else
8379         {
8380             CmpName = Name;
8381             CmpPrefix = &Entry->Prefix;
8382             CaseInsensitive = ThisTable->CaseInsensitiveMatch;
8383         }
8384 
8385         /* Perform the compare, if there's a match, also check for connection ID */
8386         if (RtlEqualUnicodeString(CmpName, CmpPrefix, CaseInsensitive))
8387         {
8388             if (!ThisTable->IsNetNameTable || RxEqualConnectionId(RxConnectionId, &Entry->ConnectionId))
8389             {
8390                 return Entry;
8391             }
8392         }
8393     }
8394 
8395     return NULL;
8396 }
8397 
8398 /*
8399  * @implemented
8400  */
8401 NTSTATUS
8402 RxTearDownBufferingManager(
8403    PSRV_CALL SrvCall)
8404 {
8405     PAGED_CODE();
8406 
8407     /* Nothing to do */
8408     return STATUS_SUCCESS;
8409 }
8410 
8411 /*
8412  * @implemented
8413  */
8414 VOID
8415 NTAPI
8416 RxTimerDispatch(
8417     _In_ struct _KDPC *Dpc,
8418     _In_opt_ PVOID DeferredContext,
8419     _In_opt_ PVOID SystemArgument1,
8420     _In_opt_ PVOID SystemArgument2)
8421 {
8422     BOOLEAN Set;
8423     LIST_ENTRY LocalList;
8424     PLIST_ENTRY ListEntry;
8425     PRX_WORK_ITEM WorkItem;
8426 
8427     InitializeListHead(&LocalList);
8428 
8429     KeAcquireSpinLockAtDpcLevel(&RxTimerLock);
8430     ++RxTimerTickCount;
8431 
8432     /* Find any entry matching */
8433     if (!IsListEmpty(&RxTimerQueueHead))
8434     {
8435         ListEntry = RxTimerQueueHead.Flink;
8436         do
8437         {
8438             WorkItem = CONTAINING_RECORD(ListEntry, RX_WORK_ITEM, WorkQueueItem.List);
8439             if (WorkItem->LastTick == RxTimerTickCount)
8440             {
8441                 ListEntry = ListEntry->Flink;
8442 
8443                 RemoveEntryList(&WorkItem->WorkQueueItem.List);
8444                 InsertTailList(&LocalList, &WorkItem->WorkQueueItem.List);
8445             }
8446             else
8447             {
8448                 ListEntry = ListEntry->Flink;
8449             }
8450         } while (ListEntry != &RxTimerQueueHead);
8451     }
8452     /* Do we have to requeue a later execution? */
8453     Set = !IsListEmpty(&RxTimerQueueHead);
8454 
8455     KeReleaseSpinLockFromDpcLevel(&RxTimerLock);
8456 
8457     /* Requeue if list wasn't empty */
8458     if (Set)
8459     {
8460         KeSetTimer(&RxTimer, RxTimerInterval, &RxTimerDpc);
8461     }
8462 
8463     /* If we had matching entries */
8464     if (!IsListEmpty(&LocalList))
8465     {
8466         /* Post them, one after another */
8467         ListEntry = LocalList.Flink;
8468         do
8469         {
8470             WorkItem = CONTAINING_RECORD(ListEntry, RX_WORK_ITEM, WorkQueueItem.List);
8471             ListEntry = ListEntry->Flink;
8472 
8473             WorkItem->WorkQueueItem.List.Flink = NULL;
8474             WorkItem->WorkQueueItem.List.Blink = NULL;
8475             RxPostToWorkerThread(WorkItem->WorkQueueItem.pDeviceObject, CriticalWorkQueue,
8476                                  &WorkItem->WorkQueueItem, WorkItem->WorkQueueItem.WorkerRoutine,
8477                                  WorkItem->WorkQueueItem.Parameter);
8478         }
8479         while (ListEntry != &LocalList);
8480     }
8481 }
8482 
8483 #ifdef RDBSS_TRACKER
8484 /*
8485  * @implemented
8486  */
8487 VOID
8488 RxTrackerUpdateHistory(
8489     _Inout_opt_ PRX_CONTEXT RxContext,
8490     _Inout_ PMRX_FCB MrxFcb,
8491     _In_ ULONG Operation,
8492     _In_ ULONG LineNumber,
8493     _In_ PCSTR FileName,
8494     _In_ ULONG SerialNumber)
8495 {
8496     PFCB Fcb;
8497     RX_FCBTRACKER_CASES Case;
8498 
8499     /* Check for null or special context */
8500     if (RxContext == NULL)
8501     {
8502         Case = RX_FCBTRACKER_CASE_NULLCONTEXT;
8503     }
8504     else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT)
8505     {
8506         Case = RX_FCBTRACKER_CASE_CBS_CONTEXT;
8507     }
8508     else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
8509     {
8510         Case = RX_FCBTRACKER_CASE_CBS_WAIT_CONTEXT;
8511     }
8512     else
8513     {
8514         ASSERT(NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT);
8515         Case = RX_FCBTRACKER_CASE_NORMAL;
8516     }
8517 
8518     /* If caller provided a FCB, update its history */
8519     if (MrxFcb != NULL)
8520     {
8521         Fcb = (PFCB)MrxFcb;
8522         ASSERT(NodeTypeIsFcb(Fcb));
8523 
8524         /* Only one acquire operation, so many release operations... */
8525         if (Operation == TRACKER_ACQUIRE_FCB)
8526         {
8527             ++Fcb->FcbAcquires[Case];
8528         }
8529         else
8530         {
8531             ++Fcb->FcbReleases[Case];
8532         }
8533     }
8534 
8535     /* If we have a normal context, update its history about this function calls */
8536     if (Case == RX_FCBTRACKER_CASE_NORMAL)
8537     {
8538         ULONG TrackerHistoryPointer;
8539 
8540         /* Only one acquire operation, so many release operations... */
8541         if (Operation == TRACKER_ACQUIRE_FCB)
8542         {
8543             InterlockedIncrement(&RxContext->AcquireReleaseFcbTrackerX);
8544         }
8545         else
8546         {
8547             InterlockedDecrement(&RxContext->AcquireReleaseFcbTrackerX);
8548         }
8549 
8550         /* We only keep track of the 32 first calls */
8551         TrackerHistoryPointer = InterlockedExchangeAdd((volatile long *)&RxContext->TrackerHistoryPointer, 1);
8552         if (TrackerHistoryPointer < RDBSS_TRACKER_HISTORY_SIZE)
8553         {
8554             RxContext->TrackerHistory[TrackerHistoryPointer].AcquireRelease = Operation;
8555             RxContext->TrackerHistory[TrackerHistoryPointer].LineNumber = LineNumber;
8556             RxContext->TrackerHistory[TrackerHistoryPointer].FileName = (PSZ)FileName;
8557             RxContext->TrackerHistory[TrackerHistoryPointer].SavedTrackerValue = RxContext->AcquireReleaseFcbTrackerX;
8558             RxContext->TrackerHistory[TrackerHistoryPointer].Flags = RxContext->Flags;
8559         }
8560 
8561         /* If it's negative, then we released once more than we acquired it?! */
8562         ASSERT(RxContext->AcquireReleaseFcbTrackerX >= 0);
8563     }
8564 }
8565 #endif
8566 
8567 VOID
8568 RxTrackPagingIoResource(
8569     _Inout_ PVOID Instance,
8570     _In_ ULONG Type,
8571     _In_ ULONG Line,
8572     _In_ PCSTR File)
8573 {
8574     UNIMPLEMENTED;
8575 }
8576 
8577 /*
8578  * @implemented
8579  */
8580 VOID
8581 RxUndoScavengerFinalizationMarking(
8582     PVOID Instance)
8583 {
8584     /* Just call internal routine with mutex held */
8585     RxAcquireScavengerMutex();
8586     RxpUndoScavengerFinalizationMarking(Instance);
8587     RxReleaseScavengerMutex();
8588 }
8589 
8590 /*
8591  * @implemented
8592  */
8593 VOID
8594 RxUninitializeVNetRootParameters(
8595    IN PUNICODE_STRING UserName,
8596    IN PUNICODE_STRING UserDomainName,
8597    IN PUNICODE_STRING Password,
8598    OUT PULONG Flags)
8599 {
8600     PAGED_CODE();
8601 
8602     /* Only free what could have been allocated */
8603     if (UserName != NULL)
8604     {
8605         RxFreePool(UserName);
8606     }
8607 
8608     if (UserDomainName != NULL)
8609     {
8610         RxFreePool(UserDomainName);
8611     }
8612 
8613     if (Password != NULL)
8614     {
8615         RxFreePool(Password);
8616     }
8617 
8618     /* And remove the possibly set CSC agent flag */
8619     if (Flags != NULL)
8620     {
8621         (*Flags) &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE;
8622     }
8623 }
8624 
8625 /*
8626  * @implemented
8627  */
8628 VOID
8629 RxUpdateCondition(
8630     IN RX_BLOCK_CONDITION NewConditionValue,
8631     OUT PRX_BLOCK_CONDITION Condition,
8632     IN OUT PLIST_ENTRY TransitionWaitList)
8633 {
8634     PRX_CONTEXT Context;
8635     LIST_ENTRY SerializationQueue;
8636 
8637     PAGED_CODE();
8638 
8639     DPRINT("RxUpdateCondition(%d, %p, %p)\n", NewConditionValue, Condition, TransitionWaitList);
8640 
8641     /* Set the new condition */
8642     RxAcquireSerializationMutex();
8643     ASSERT(NewConditionValue != Condition_InTransition);
8644     *Condition = NewConditionValue;
8645     /* And get the serialization queue for treatment */
8646     RxTransferList(&SerializationQueue, TransitionWaitList);
8647     RxReleaseSerializationMutex();
8648 
8649     /* Handle the serialization queue */
8650     Context = RxRemoveFirstContextFromSerializationQueue(&SerializationQueue);
8651     while (Context != NULL)
8652     {
8653         /* If the caller asked for post, post the request */
8654         if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION))
8655         {
8656             Context->Flags &= ~RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION;
8657             RxFsdPostRequest(Context);
8658         }
8659         /* Otherwise, wake up sleeping waiters */
8660         else
8661         {
8662             RxSignalSynchronousWaiter(Context);
8663         }
8664 
8665         Context = RxRemoveFirstContextFromSerializationQueue(&SerializationQueue);
8666     }
8667 }
8668 
8669 /*
8670  * @implemented
8671  */
8672 VOID
8673 RxVerifyOperationIsLegal(
8674     IN PRX_CONTEXT RxContext)
8675 {
8676     PIRP Irp;
8677     PMRX_FOBX Fobx;
8678     BOOLEAN FlagSet;
8679     PFILE_OBJECT FileObject;
8680     PIO_STACK_LOCATION Stack;
8681 
8682     PAGED_CODE();
8683 
8684     Irp = RxContext->CurrentIrp;
8685     Stack = RxContext->CurrentIrpSp;
8686     FileObject = Stack->FileObject;
8687 
8688     /* We'll only check stuff on opened files, this requires an IRP and a FO */
8689     if (Irp == NULL || FileObject == NULL)
8690     {
8691         return;
8692     }
8693 
8694     /* Set no exception for breakpoint - remember whether is was already set */
8695     FlagSet = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
8696     SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
8697 
8698     /* If we have a CCB, perform a few checks on opened file */
8699     Fobx = RxContext->pFobx;
8700     if (Fobx != NULL)
8701     {
8702         PMRX_SRV_OPEN SrvOpen;
8703 
8704         SrvOpen = Fobx->pSrvOpen;
8705         if (SrvOpen != NULL)
8706         {
8707             UCHAR MajorFunction;
8708 
8709             MajorFunction = RxContext->MajorFunction;
8710             /* Only allow closing/cleanup operations on renamed files */
8711             if (MajorFunction != IRP_MJ_CLEANUP && MajorFunction != IRP_MJ_CLOSE &&
8712                 BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_RENAMED))
8713             {
8714                 RxContext->IoStatusBlock.Status = STATUS_FILE_RENAMED;
8715                 ExRaiseStatus(STATUS_FILE_RENAMED);
8716             }
8717 
8718             /* Only allow closing/cleanup operations on deleted files */
8719             if (MajorFunction != IRP_MJ_CLEANUP && MajorFunction != IRP_MJ_CLOSE &&
8720                 BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_DELETED))
8721             {
8722                 RxContext->IoStatusBlock.Status = STATUS_FILE_DELETED;
8723                 ExRaiseStatus(STATUS_FILE_DELETED);
8724             }
8725         }
8726     }
8727 
8728     /* If that's an open operation */
8729     if (RxContext->MajorFunction == IRP_MJ_CREATE)
8730     {
8731         PFILE_OBJECT RelatedFileObject;
8732 
8733         /* We won't allow an open operation relative to a file to be deleted */
8734         RelatedFileObject = FileObject->RelatedFileObject;
8735         if (RelatedFileObject != NULL)
8736         {
8737             PMRX_FCB Fcb;
8738 
8739             Fcb = RelatedFileObject->FsContext;
8740             if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE))
8741             {
8742                 RxContext->IoStatusBlock.Status = STATUS_DELETE_PENDING;
8743                 ExRaiseStatus(STATUS_DELETE_PENDING);
8744             }
8745         }
8746     }
8747 
8748     /* If cleanup was completed */
8749     if (BooleanFlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
8750     {
8751         if (!BooleanFlagOn(Irp->Flags, IRP_PAGING_IO))
8752         {
8753             UCHAR MajorFunction;
8754 
8755             /* We only allow a subset of operations (see FatVerifyOperationIsLegal for instance) */
8756             MajorFunction = Stack->MajorFunction;
8757             if (MajorFunction != IRP_MJ_CLOSE && MajorFunction != IRP_MJ_QUERY_INFORMATION &&
8758                 MajorFunction != IRP_MJ_SET_INFORMATION)
8759             {
8760                 if ((MajorFunction != IRP_MJ_READ && MajorFunction != IRP_MJ_WRITE) ||
8761                     !BooleanFlagOn(Stack->MinorFunction, IRP_MN_COMPLETE))
8762                 {
8763                     RxContext->IoStatusBlock.Status = STATUS_FILE_CLOSED;
8764                     ExRaiseStatus(STATUS_FILE_CLOSED);
8765                 }
8766             }
8767         }
8768     }
8769 
8770     /* If flag was already set, don't clear it */
8771     if (!FlagSet)
8772     {
8773         ClearFlag(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
8774     }
8775 }
8776 
8777 /*
8778  * @implemented
8779  */
8780 VOID
8781 RxWaitForStableCondition(
8782     IN PRX_BLOCK_CONDITION Condition,
8783     IN OUT PLIST_ENTRY TransitionWaitList,
8784     IN OUT PRX_CONTEXT RxContext,
8785     OUT NTSTATUS *AsyncStatus OPTIONAL)
8786 {
8787     BOOLEAN Wait;
8788     NTSTATUS LocalStatus;
8789 
8790     PAGED_CODE();
8791 
8792     /* Make sure to always get status */
8793     if (AsyncStatus == NULL)
8794     {
8795         AsyncStatus = &LocalStatus;
8796     }
8797 
8798     /* By default, it's a success */
8799     *AsyncStatus = STATUS_SUCCESS;
8800 
8801     Wait = FALSE;
8802     /* If it's not stable, we've to wait */
8803     if (!StableCondition(*Condition))
8804     {
8805         /* Lock the mutex */
8806         RxAcquireSerializationMutex();
8807         /* Still not stable? */
8808         if (!StableCondition(*Condition))
8809         {
8810             /* Insert us in the wait list for processing on stable condition */
8811             RxInsertContextInSerializationQueue(TransitionWaitList, RxContext);
8812 
8813             /* If we're asked to post on stable, don't wait, and just return pending */
8814             if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION))
8815             {
8816                 *AsyncStatus = STATUS_PENDING;
8817             }
8818             else
8819             {
8820                 Wait = TRUE;
8821             }
8822         }
8823         RxReleaseSerializationMutex();
8824 
8825         /* We don't post on stable, so, just wait... */
8826         if (Wait)
8827         {
8828             RxWaitSync(RxContext);
8829         }
8830     }
8831 }
8832 
8833 /*
8834  * @implemented
8835  */
8836 VOID
8837 NTAPI
8838 RxWorkItemDispatcher(
8839     PVOID Context)
8840 {
8841     PRX_WORK_DISPATCH_ITEM DispatchItem = Context;
8842 
8843     DPRINT("Calling: %p, %p\n", DispatchItem->DispatchRoutine, DispatchItem->DispatchRoutineParameter);
8844 
8845     DispatchItem->DispatchRoutine(DispatchItem->DispatchRoutineParameter);
8846 
8847     RxFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
8848 }
8849 
8850 /*
8851  * @implemented
8852  */
8853 PVOID
8854 NTAPI
8855 _RxAllocatePoolWithTag(
8856     _In_ POOL_TYPE PoolType,
8857     _In_ SIZE_T NumberOfBytes,
8858     _In_ ULONG Tag)
8859 {
8860     return ExAllocatePoolWithTagPriority(PoolType, NumberOfBytes, Tag, LowPoolPriority);
8861 }
8862 
8863 /*
8864  * @implemented
8865  */
8866 VOID
8867 NTAPI
8868 _RxFreePool(
8869     _In_ PVOID Buffer)
8870 {
8871     ExFreePoolWithTag(Buffer, 0);
8872 }
8873 
8874 /*
8875  * @implemented
8876  */
8877 VOID
8878 NTAPI
8879 _RxFreePoolWithTag(
8880     _In_ PVOID Buffer,
8881     _In_ ULONG Tag)
8882 {
8883     ExFreePoolWithTag(Buffer, Tag);
8884 }
8885 
8886 NTSTATUS
8887 __RxAcquireFcb(
8888     _Inout_ PFCB Fcb,
8889     _Inout_opt_ PRX_CONTEXT RxContext OPTIONAL,
8890     _In_ ULONG Mode
8891 #ifdef RDBSS_TRACKER
8892     ,
8893     _In_ ULONG LineNumber,
8894     _In_ PCSTR FileName,
8895     _In_ ULONG SerialNumber
8896 #endif
8897     )
8898 {
8899     NTSTATUS Status;
8900     BOOLEAN SpecialContext, CanWait, Acquired, ContextIsPresent;
8901 
8902     PAGED_CODE();
8903 
8904     DPRINT("__RxAcquireFcb(%p, %p, %d, %d, %s, %d)\n", Fcb, RxContext, Mode, LineNumber, FileName, SerialNumber);
8905 
8906     SpecialContext = FALSE;
8907     ContextIsPresent = FALSE;
8908     /* Check for special context */
8909     if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT || RxContext == CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
8910     {
8911         SpecialContext = TRUE;
8912     }
8913 
8914     /* We don't handle buffering state change yet... */
8915     if (!RxIsFcbAcquired(Fcb) && !SpecialContext &&
8916         BooleanFlagOn(Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING))
8917     {
8918         UNIMPLEMENTED;
8919     }
8920 
8921     /* Nor special contexts */
8922     if (SpecialContext)
8923     {
8924         UNIMPLEMENTED;
8925     }
8926 
8927     /* If we don't have a context, assume we can wait! */
8928     if (RxContext == NULL)
8929     {
8930         CanWait = TRUE;
8931     }
8932     else
8933     {
8934         /* That said: we have a real context! */
8935         ContextIsPresent = TRUE;
8936 
8937         /* If we've been cancelled in between, give up */
8938         Status = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED) ? STATUS_CANCELLED : STATUS_SUCCESS;
8939         if (!NT_SUCCESS(Status))
8940         {
8941             return Status;
8942         }
8943 
8944         /* Can we wait? */
8945         CanWait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
8946     }
8947 
8948     while (TRUE)
8949     {
8950         /* Assume we cannot lock */
8951         Status = STATUS_LOCK_NOT_GRANTED;
8952 
8953         /* Lock according to what the caller asked */
8954         switch (Mode)
8955         {
8956             case FCB_MODE_EXCLUSIVE:
8957                 Acquired = ExAcquireResourceExclusiveLite(Fcb->Header.Resource, CanWait);
8958                 break;
8959 
8960             case FCB_MODE_SHARED:
8961                 Acquired = ExAcquireResourceSharedLite(Fcb->Header.Resource, CanWait);
8962                 break;
8963 
8964             case FCB_MODE_SHARED_WAIT_FOR_EXCLUSIVE:
8965                 Acquired = ExAcquireSharedWaitForExclusive(Fcb->Header.Resource, CanWait);
8966                 break;
8967 
8968             default:
8969                 ASSERT(Mode == FCB_MODE_SHARED_STARVE_EXCLUSIVE);
8970                 Acquired = ExAcquireSharedStarveExclusive(Fcb->Header.Resource, CanWait);
8971                 break;
8972         }
8973 
8974         /* Lock granted! */
8975         if (Acquired)
8976         {
8977             Status = STATUS_SUCCESS;
8978             ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
8979 
8980             /* Handle paging write - not implemented */
8981             if (Fcb->NonPaged->OutstandingAsyncWrites != 0)
8982             {
8983                 UNIMPLEMENTED;
8984             }
8985         }
8986 
8987         /* And break, that cool! */
8988         if (Acquired)
8989         {
8990             break;
8991         }
8992 
8993         /* If it failed, return immediately */
8994         if (!NT_SUCCESS(Status))
8995         {
8996             return Status;
8997         }
8998     }
8999 
9000     /* If we don't have to check for valid operation, job done, nothing more to do */
9001     if (!ContextIsPresent || BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK))
9002     {
9003         if (NT_SUCCESS(Status))
9004         {
9005             RxTrackerUpdateHistory(RxContext, RX_GET_MRX_FCB(Fcb), TRACKER_ACQUIRE_FCB, LineNumber, FileName, SerialNumber);
9006         }
9007 
9008         return Status;
9009     }
9010 
9011     /* Verify operation */
9012     _SEH2_TRY
9013     {
9014         RxVerifyOperationIsLegal(RxContext);
9015     }
9016     _SEH2_FINALLY
9017     {
9018         /* If it failed, release lock and fail */
9019         if (_SEH2_AbnormalTermination())
9020         {
9021             ExReleaseResourceLite(Fcb->Header.Resource);
9022             Status = STATUS_LOCK_NOT_GRANTED;
9023         }
9024     }
9025     _SEH2_END;
9026 
9027     if (NT_SUCCESS(Status))
9028     {
9029         RxTrackerUpdateHistory(RxContext, RX_GET_MRX_FCB(Fcb), TRACKER_ACQUIRE_FCB, LineNumber, FileName, SerialNumber);
9030     }
9031 
9032     DPRINT("Status: %x\n", Status);
9033     return Status;
9034 }
9035 
9036 /*
9037  * @implemented
9038  */
9039 VOID
9040 __RxItsTheSameContext(
9041     _In_ PRX_CONTEXT RxContext,
9042     _In_ ULONG CapturedRxContextSerialNumber,
9043     _In_ ULONG Line,
9044     _In_ PCSTR File)
9045 {
9046     /* Check we have a context with the same serial number */
9047     if (NodeType(RxContext) != RDBSS_NTC_RX_CONTEXT ||
9048         RxContext->SerialNumber != CapturedRxContextSerialNumber)
9049     {
9050         /* Just be noisy */
9051         DPRINT1("Context %p has changed at line %d in file %s\n", RxContext, Line, File);
9052     }
9053 }
9054 
9055 VOID
9056 __RxReleaseFcb(
9057     _Inout_opt_ PRX_CONTEXT RxContext,
9058     _Inout_ PMRX_FCB MrxFcb
9059 #ifdef RDBSS_TRACKER
9060     ,
9061     _In_ ULONG LineNumber,
9062     _In_ PCSTR FileName,
9063     _In_ ULONG SerialNumber
9064 #endif
9065     )
9066 {
9067     BOOLEAN IsExclusive, BufferingPending;
9068 
9069     RxAcquireSerializationMutex();
9070 
9071     BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
9072     IsExclusive = !!RxIsResourceOwnershipStateExclusive(MrxFcb->Header.Resource);
9073 
9074     /* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
9075      * then just release the FCB
9076      */
9077     if (!BufferingPending || !IsExclusive)
9078     {
9079         RxTrackerUpdateHistory(RxContext, MrxFcb, (!BufferingPending ? TRACKER_RELEASE_FCB_NO_BUFF_PENDING : TRACKER_RELEASE_NON_EXCL_FCB_BUFF_PENDING),
9080                                LineNumber, FileName, SerialNumber);
9081         ExReleaseResourceLite(MrxFcb->Header.Resource);
9082     }
9083 
9084     RxReleaseSerializationMutex();
9085 
9086     /* And finally leave */
9087     if (!BufferingPending || !IsExclusive)
9088     {
9089         return;
9090     }
9091 
9092     ASSERT(RxIsFcbAcquiredExclusive(MrxFcb));
9093 
9094     /* Otherwise, handle buffering state and release */
9095     RxProcessFcbChangeBufferingStateRequest((PFCB)MrxFcb);
9096 
9097     RxTrackerUpdateHistory(RxContext, MrxFcb, TRACKER_RELEASE_EXCL_FCB_BUFF_PENDING, LineNumber, FileName, SerialNumber);
9098     ExReleaseResourceLite(MrxFcb->Header.Resource);
9099 }
9100 
9101 VOID
9102 __RxReleaseFcbForThread(
9103     _Inout_opt_ PRX_CONTEXT RxContext,
9104     _Inout_ PMRX_FCB MrxFcb,
9105     _In_ ERESOURCE_THREAD ResourceThreadId
9106 #ifdef RDBSS_TRACKER
9107     ,
9108     _In_ ULONG LineNumber,
9109     _In_ PCSTR FileName,
9110     _In_ ULONG SerialNumber
9111 #endif
9112     )
9113 {
9114     BOOLEAN IsExclusive, BufferingPending;
9115 
9116     RxAcquireSerializationMutex();
9117 
9118     BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
9119     IsExclusive = !!RxIsResourceOwnershipStateExclusive(MrxFcb->Header.Resource);
9120 
9121     /* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
9122      * then just release the FCB
9123      */
9124     if (!BufferingPending || !IsExclusive)
9125     {
9126         RxTrackerUpdateHistory(RxContext, MrxFcb,
9127                                (!BufferingPending ? TRACKER_RELEASE_FCB_FOR_THRD_NO_BUFF_PENDING : TRACKER_RELEASE_NON_EXCL_FCB_FOR_THRD_BUFF_PENDING),
9128                                LineNumber, FileName, SerialNumber);
9129         ExReleaseResourceForThreadLite(MrxFcb->Header.Resource, ResourceThreadId);
9130     }
9131 
9132     RxReleaseSerializationMutex();
9133 
9134     /* And finally leave */
9135     if (!BufferingPending || !IsExclusive)
9136     {
9137         return;
9138     }
9139 
9140     /* Otherwise, handle buffering state and release */
9141     RxTrackerUpdateHistory(RxContext, MrxFcb, TRACKER_RELEASE_EXCL_FCB_FOR_THRD_BUFF_PENDING, LineNumber, FileName, SerialNumber);
9142     RxProcessFcbChangeBufferingStateRequest((PFCB)MrxFcb);
9143     ExReleaseResourceForThreadLite(MrxFcb->Header.Resource, ResourceThreadId);
9144 }
9145