reactos/reactos/sdk/lib/drivers/rxce/rxce.c
Pierre Schweitzer acc9f33775 [RXCE]
- Make RxpUndoScavengerFinalizationMarking() more smart: only warn about unimplemented if there's scavenger mark
- Implement FCB operations (release/acquire) tracker

CORE-11327

svn path=/trunk/; revision=74678
2017-05-27 09:36:42 +00:00

5775 lines
168 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2017 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: sdk/lib/drivers/rxce/rxce.c
* PURPOSE: RXCE library
* PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <rx.h>
#include <pseh/pseh2.h>
#include <dfs.h>
#define NDEBUG
#include <debug.h>
VOID
RxAssert(
PVOID Assert,
PVOID File,
ULONG Line,
PVOID Message);
VOID
NTAPI
RxCreateSrvCallCallBack(
IN OUT PMRX_SRVCALL_CALLBACK_CONTEXT Context);
NTSTATUS
RxFinishSrvCallConstruction(
PMRX_SRVCALLDOWN_STRUCTURE Calldown);
VOID
NTAPI
RxFinishSrvCallConstructionDispatcher(
IN PVOID Context);
NTSTATUS
RxInsertWorkQueueItem(
PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
WORK_QUEUE_TYPE WorkQueueType,
PRX_WORK_DISPATCH_ITEM DispatchItem);
PVOID
RxNewMapUserBuffer(
PRX_CONTEXT RxContext);
VOID
NTAPI
RxWorkItemDispatcher(
PVOID Context);
extern ULONG ReadAheadGranularity;
volatile LONG RxNumberOfActiveFcbs = 0;
ULONG SerialNumber = 1;
PVOID RxNull = NULL;
volatile ULONG RxContextSerialNumberCounter;
BOOLEAN RxStopOnLoudCompletion = TRUE;
BOOLEAN RxSrvCallConstructionDispatcherActive = FALSE;
LIST_ENTRY RxSrvCalldownList;
RX_SPIN_LOCK RxStrucSupSpinLock;
ULONG RdbssReferenceTracingValue;
LARGE_INTEGER RxWorkQueueWaitInterval[RxMaximumWorkQueue];
LARGE_INTEGER RxSpinUpDispatcherWaitInterval;
RX_DISPATCHER RxDispatcher;
RX_WORK_QUEUE_DISPATCHER RxDispatcherWorkQueues;
FAST_MUTEX RxLowIoPagingIoSyncMutex;
BOOLEAN RxContinueFromAssert = TRUE;
#if DBG
BOOLEAN DumpDispatchRoutine = TRUE;
#else
BOOLEAN DumpDispatchRoutine = FALSE;
#endif
#ifdef ASSERT
#undef ASSERT
#endif
#define ASSERT(exp) \
if (!(exp)) \
{ \
RxAssert(#exp, __FILE__, __LINE__, NULL); \
}
/* FUNCTIONS ****************************************************************/
/*
* @implemented
*/
VOID
RxAddVirtualNetRootToNetRoot(
PNET_ROOT NetRoot,
PV_NET_ROOT VNetRoot)
{
PAGED_CODE();
DPRINT("RxAddVirtualNetRootToNetRoot(%p, %p)\n", NetRoot, VNetRoot);
/* Insert in the VNetRoot list - make sure lock is held */
ASSERT(RxIsPrefixTableLockExclusive(NetRoot->SrvCall->RxDeviceObject->pRxNetNameTable));
VNetRoot->pNetRoot = (PMRX_NET_ROOT)NetRoot;
++NetRoot->NumberOfVirtualNetRoots;
InsertTailList(&NetRoot->VirtualNetRoots, &VNetRoot->NetRootListEntry);
}
/*
* @implemented
*/
PVOID
RxAllocateFcbObject(
PRDBSS_DEVICE_OBJECT RxDeviceObject,
NODE_TYPE_CODE NodeType,
POOL_TYPE PoolType,
ULONG NameSize,
PVOID AlreadyAllocatedObject)
{
PFCB Fcb;
PFOBX Fobx;
PSRV_OPEN SrvOpen;
PVOID Buffer, PAPNBuffer;
PNON_PAGED_FCB NonPagedFcb;
PMINIRDR_DISPATCH Dispatch;
ULONG NonPagedSize, FobxSize, SrvOpenSize, FcbSize;
PAGED_CODE();
Dispatch = RxDeviceObject->Dispatch;
NonPagedSize = 0;
FobxSize = 0;
SrvOpenSize = 0;
FcbSize = 0;
Fcb = NULL;
Fobx = NULL;
SrvOpen = NULL;
NonPagedFcb = NULL;
PAPNBuffer = NULL;
/* If we ask for FOBX, just allocate FOBX and its extension if asked */
if (NodeType == RDBSS_NTC_FOBX)
{
FobxSize = sizeof(FOBX);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
{
FobxSize += QuadAlign(Dispatch->MRxFobxSize);
}
}
/* If we ask for SRV_OPEN, also allocate the "internal" FOBX and the extensions if asked */
else if (NodeType == RDBSS_NTC_SRVOPEN || NodeType == RDBSS_NTC_INTERNAL_SRVOPEN)
{
SrvOpenSize = sizeof(SRV_OPEN);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
{
SrvOpenSize += QuadAlign(Dispatch->MRxSrvOpenSize);
}
FobxSize = sizeof(FOBX);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
{
FobxSize += QuadAlign(Dispatch->MRxFobxSize);
}
}
/* Otherwise, we're asked to allocate a FCB */
else
{
/* So, allocate the FCB and its extension if asked */
FcbSize = sizeof(FCB);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FCB_EXTENSION))
{
FcbSize += QuadAlign(Dispatch->MRxFcbSize);
}
/* If we're asked to allocate from nonpaged, also allocate the NON_PAGED_FCB
* Otherwise, it will be allocated later on, specifically
*/
if (PoolType == NonPagedPool)
{
NonPagedSize = sizeof(NON_PAGED_FCB);
}
/* And if it's not for a rename operation also allcoate the internal SRV_OPEN and FOBX and their extensions */
if (NodeType != RDBSS_NTC_OPENTARGETDIR_FCB)
{
SrvOpenSize = sizeof(SRV_OPEN);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
{
SrvOpenSize += QuadAlign(Dispatch->MRxSrvOpenSize);
}
FobxSize = sizeof(FOBX);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
{
FobxSize += QuadAlign(Dispatch->MRxFobxSize);
}
}
}
/* If we already have a buffer, go ahead */
if (AlreadyAllocatedObject != NULL)
{
Buffer = AlreadyAllocatedObject;
}
/* Otherwise, allocate it */
else
{
Buffer = ExAllocatePoolWithTag(PoolType, NameSize + FcbSize + SrvOpenSize + FobxSize + NonPagedSize, RX_FCB_POOLTAG);
if (Buffer == NULL)
{
return NULL;
}
}
/* Now, get the pointers - FOBX is easy */
if (NodeType == RDBSS_NTC_FOBX)
{
Fobx = Buffer;
}
/* SRV_OPEN first, FOBX next */
else if (NodeType == RDBSS_NTC_SRVOPEN)
{
SrvOpen = Buffer;
Fobx = Add2Ptr(Buffer, SrvOpenSize);
}
else if (NodeType == RDBSS_NTC_INTERNAL_SRVOPEN)
{
SrvOpen = Buffer;
}
else
{
/* FCB first, and if needed, SRV_OPEN next, FOBX last */
Fcb = Buffer;
if (NodeType != RDBSS_NTC_OPENTARGETDIR_FCB)
{
SrvOpen = Add2Ptr(Buffer, FcbSize);
Fobx = Add2Ptr(Buffer, FcbSize + SrvOpenSize);
}
/* If we were not allocated from non paged, allocate the NON_PAGED_FCB now */
if (PoolType != NonPagedPool)
{
NonPagedFcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(NON_PAGED_FCB), RX_NONPAGEDFCB_POOLTAG);
if (NonPagedFcb == NULL)
{
ExFreePoolWithTag(Buffer, RX_FCB_POOLTAG);
return NULL;
}
PAPNBuffer = Add2Ptr(Buffer, FcbSize + SrvOpenSize + FobxSize);
}
/* Otherwise, just point at the right place in what has been allocated previously */
else
{
NonPagedFcb = Add2Ptr(Fobx, FobxSize);
PAPNBuffer = Add2Ptr(Fobx, FobxSize + NonPagedSize);
}
}
/* If we have allocated a SRV_OPEN, initialize it */
if (SrvOpen != NULL)
{
ZeroAndInitializeNodeType(SrvOpen, RDBSS_NTC_SRVOPEN, SrvOpenSize);
if (NodeType == RDBSS_NTC_SRVOPEN)
{
SrvOpen->InternalFobx = Fobx;
}
else
{
SrvOpen->InternalFobx = NULL;
SrvOpen->Flags |= SRVOPEN_FLAG_FOBX_USED;
}
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_SRV_OPEN_EXTENSION))
{
SrvOpen->Context = Add2Ptr(SrvOpen, sizeof(SRV_OPEN));
}
InitializeListHead(&SrvOpen->SrvOpenQLinks);
}
/* If we have allocated a FOBX, initialize it */
if (Fobx != NULL)
{
ZeroAndInitializeNodeType(Fobx, RDBSS_NTC_FOBX, FobxSize);
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FOBX_EXTENSION))
{
Fobx->Context = Add2Ptr(Fobx, sizeof(FOBX));
}
}
/* If we have allocated a FCB, initialize it */
if (Fcb != NULL)
{
ZeroAndInitializeNodeType(Fcb, RDBSS_STORAGE_NTC(FileTypeNotYetKnown), FcbSize);
Fcb->NonPaged = NonPagedFcb;
ZeroAndInitializeNodeType(Fcb->NonPaged, RDBSS_NTC_NONPAGED_FCB, sizeof(NON_PAGED_FCB));
Fcb->CopyOfNonPaged = NonPagedFcb;
NonPagedFcb->FcbBackPointer = Fcb;
Fcb->InternalSrvOpen = SrvOpen;
Fcb->InternalFobx = Fobx;
Fcb->PrivateAlreadyPrefixedName.Length = NameSize;
Fcb->PrivateAlreadyPrefixedName.MaximumLength = NameSize;
Fcb->PrivateAlreadyPrefixedName.Buffer = PAPNBuffer;
if (BooleanFlagOn(Dispatch->MRxFlags, RDBSS_MANAGE_FCB_EXTENSION))
{
Fcb->Context = Add2Ptr(Fcb, sizeof(FCB));
}
ZeroAndInitializeNodeType(&Fcb->FcbTableEntry, RDBSS_NTC_FCB_TABLE_ENTRY, sizeof(RX_FCB_TABLE_ENTRY));
InterlockedIncrement(&RxNumberOfActiveFcbs);
InterlockedIncrement((volatile long *)&RxDeviceObject->NumberOfActiveFcbs);
ExInitializeFastMutex(&NonPagedFcb->AdvancedFcbHeaderMutex);
FsRtlSetupAdvancedHeader(Fcb, &NonPagedFcb->AdvancedFcbHeaderMutex);
}
return Buffer;
}
/*
* @implemented
*/
PVOID
RxAllocateObject(
NODE_TYPE_CODE NodeType,
PMINIRDR_DISPATCH MRxDispatch,
ULONG NameLength)
{
ULONG Tag, ObjectSize;
PVOID Object, *Extension;
PRX_PREFIX_ENTRY PrefixEntry;
USHORT StructSize, ExtensionSize;
PAGED_CODE();
/* Select the node to allocate and always deal with the fact we may have to manage its extension */
ExtensionSize = 0;
switch (NodeType)
{
case RDBSS_NTC_SRVCALL:
Tag = RX_SRVCALL_POOLTAG;
StructSize = sizeof(SRV_CALL);
if (MRxDispatch != NULL && BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_SRV_CALL_EXTENSION))
{
ExtensionSize = QuadAlign(MRxDispatch->MRxSrvCallSize);
}
break;
case RDBSS_NTC_NETROOT:
Tag = RX_NETROOT_POOLTAG;
StructSize = sizeof(NET_ROOT);
if (BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_NET_ROOT_EXTENSION))
{
ExtensionSize = QuadAlign(MRxDispatch->MRxNetRootSize);
}
break;
case RDBSS_NTC_V_NETROOT:
Tag = RX_V_NETROOT_POOLTAG;
StructSize = sizeof(V_NET_ROOT);
if (BooleanFlagOn(MRxDispatch->MRxFlags, RDBSS_MANAGE_V_NET_ROOT_EXTENSION))
{
ExtensionSize = QuadAlign(MRxDispatch->MRxVNetRootSize);
}
break;
default:
ASSERT(FALSE);
break;
}
/* Now, allocate the object */
ObjectSize = ExtensionSize + StructSize + NameLength;
Object = ExAllocatePoolWithTag(NonPagedPool, ObjectSize, Tag);
if (Object == NULL)
{
return NULL;
}
/* Initialize it */
ZeroAndInitializeNodeType(Object, NodeType, ObjectSize);
/* For SRV_CALL and NETROOT, the name points to the prefix table name */
switch (NodeType)
{
case RDBSS_NTC_SRVCALL:
PrefixEntry = &((PSRV_CALL)Object)->PrefixEntry;
Extension = &((PSRV_CALL)Object)->Context;
((PSRV_CALL)Object)->pSrvCallName = &PrefixEntry->Prefix;
break;
case RDBSS_NTC_NETROOT:
PrefixEntry = &((PNET_ROOT)Object)->PrefixEntry;
Extension = &((PNET_ROOT)Object)->Context;
((PNET_ROOT)Object)->pNetRootName = &PrefixEntry->Prefix;
break;
case RDBSS_NTC_V_NETROOT:
PrefixEntry = &((PV_NET_ROOT)Object)->PrefixEntry;
Extension = &((PV_NET_ROOT)Object)->Context;
break;
default:
ASSERT(FALSE);
break;
}
/* Set the prefix table unicode string */
RtlZeroMemory(PrefixEntry, sizeof(RX_PREFIX_ENTRY));
PrefixEntry->NodeTypeCode = RDBSS_NTC_PREFIX_ENTRY;
PrefixEntry->NodeByteSize = sizeof(RX_PREFIX_ENTRY);
PrefixEntry->Prefix.Length = NameLength;
PrefixEntry->Prefix.MaximumLength = NameLength;
PrefixEntry->Prefix.Buffer = Add2Ptr(Object, ExtensionSize + StructSize);
/* Return the extension if we are asked to manage it */
if (ExtensionSize != 0)
{
*Extension = Add2Ptr(Object, StructSize);
}
return Object;
}
/*
* @implemented
*/
VOID
RxAssert(
PVOID Assert,
PVOID File,
ULONG Line,
PVOID Message)
{
CHAR Response[2];
CONTEXT Context;
/* If we're not asked to continue, just stop the system */
if (!RxContinueFromAssert)
{
KeBugCheckEx(RDBSS_FILE_SYSTEM, RDBSS_BUG_CHECK_ASSERT | Line, 0, 0, 0);
}
/* Otherwise, capture context to offer the user to dump it */
RtlCaptureContext(&Context);
/* Loop until the user hits 'i' */
while (TRUE)
{
/* If no file provided, use empty name */
if (File == NULL)
{
File = "";
}
/* If no message provided, use empty one */
if (Message == NULL)
{
Message = "";
}
/* Display the message */
DbgPrint("\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", Message, Assert, File, Line);
/* And ask the user */
DbgPrompt("Break, Ignore (bi)? ", Response, sizeof(Response));
/* If he asks for ignore, quit
* In case of invalid input, ask again
*/
if (Response[0] != 'B' && Response[0] != 'b')
{
if (Response[0] == 'I' || Response[0] == 'i')
{
return;
}
continue;
}
/* Break: offer the user to dump the context and break */
DbgPrint("Execute '!cxr %lx' to dump context\n", &Context);
DbgBreakPoint();
/* Continue looping, so that after dump, execution can continue (with ignore) */
}
}
/*
* @implemented
*/
VOID
NTAPI
RxBootstrapWorkerThreadDispatcher(
IN PVOID WorkQueue)
{
PRX_WORK_QUEUE RxWorkQueue;
PAGED_CODE();
RxWorkQueue = WorkQueue;
RxpWorkerThreadDispatcher(RxWorkQueue, NULL);
}
NTSTATUS
RxCheckVNetRootCredentials(
PRX_CONTEXT RxContext,
PV_NET_ROOT VNetRoot,
PLUID LogonId,
PUNICODE_STRING UserName,
PUNICODE_STRING UserDomain,
PUNICODE_STRING Password,
ULONG Flags)
{
PAGED_CODE();
/* If that's a UNC name, there's nothing to process */
if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME) &&
(BooleanFlagOn(VNetRoot->Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE) ||
Flags != 0))
{
return STATUS_MORE_PROCESSING_REQUIRED;
}
/* Compare the logon ID in the VNetRoot with the one provided */
if (RtlCompareMemory(&VNetRoot->LogonId, LogonId, sizeof(LUID)) != sizeof(LUID))
{
return STATUS_MORE_PROCESSING_REQUIRED;
}
/* No credential provided? That's OK */
if (UserName == NULL && UserDomain == NULL && Password == NULL)
{
return STATUS_SUCCESS;
}
/* Left to do! */
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
RxCompleteRequest(
PRX_CONTEXT Context,
NTSTATUS Status)
{
PIRP Irp;
PAGED_CODE();
DPRINT("RxCompleteRequest(%p, %lx)\n", Context, Status);
ASSERT(Context != NULL);
ASSERT(Context->CurrentIrp != NULL);
Irp = Context->CurrentIrp;
/* Debug what the caller asks for */
if (Context->LoudCompletionString != NULL)
{
DPRINT("LoudCompletion: %lx/%lx with %wZ\n", Status, Irp->IoStatus.Information, Context->LoudCompletionString);
/* Does the user asks to stop on failed completion */
if (!NT_SUCCESS(Status) && RxStopOnLoudCompletion)
{
DPRINT1("LoudFailure: %lx/%lx with %wZ\n", Status, Irp->IoStatus.Information, Context->LoudCompletionString);
}
}
/* Complete for real */
Context->CurrentIrp = NULL;
RxCompleteRequest_Real(Context, Irp, Status);
DPRINT("Status: %lx\n", Status);
return Status;
}
/*
* @implemented
*/
VOID
RxCompleteRequest_Real(
IN PRX_CONTEXT RxContext,
IN PIRP Irp,
IN NTSTATUS Status)
{
CCHAR Boost;
KIRQL OldIrql;
PIO_STACK_LOCATION Stack;
DPRINT("RxCompleteRequest_Real(%p, %p, %lx)\n", RxContext, Irp, Status);
/* Nothing to complete, just free context */
if (Irp == NULL)
{
DPRINT("NULL IRP for %p\n", RxContext);
if (RxContext != NULL)
{
RxDereferenceAndDeleteRxContext_Real(RxContext);
}
return;
}
/* Remove cancel routine */
IoAcquireCancelSpinLock(&OldIrql);
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(OldIrql);
/* Select the boost, given the success/paging operation */
if (NT_SUCCESS(Status) || !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO))
{
Boost = IO_DISK_INCREMENT;
}
else
{
Irp->IoStatus.Information = 0;
Boost = IO_NO_INCREMENT;
}
Irp->IoStatus.Status = Status;
if (RxContext != NULL)
{
ASSERT(RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION);
if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL)
{
DPRINT("Completing: MN: %d, Context: %p, IRP: %p, Status: %lx, Info: %lx, #%lx\n",
RxContext->MinorFunction, RxContext, Irp,
Status, Irp->IoStatus.Information, RxContext->SerialNumber);
}
}
/* If that's an opening, there might be a canonical name allocated,
* if completion isn't pending, release it
*/
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->MajorFunction == IRP_MJ_CREATE && Status != STATUS_PENDING &&
RxContext != NULL)
{
if (BooleanFlagOn(RxContext->Create.Flags, 2))
{
Stack->FileObject->FileName.Length += sizeof(WCHAR);
}
RxpPrepareCreateContextForReuse(RxContext);
ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
}
/* If it's a write, validate the correct behavior of the operation */
if (Stack->MajorFunction == IRP_MJ_WRITE)
{
if (NT_SUCCESS(Irp->IoStatus.Status))
{
ASSERT(Irp->IoStatus.Information <= Stack->Parameters.Write.Length);
}
}
/* If it's pending, make sure IRP is marked as such */
if (RxContext != NULL)
{
if (RxContext->PendingReturned)
{
ASSERT(BooleanFlagOn(Stack->Control, SL_PENDING_RETURNED));
}
}
/* Complete now */
DPRINT("Completing IRP with %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
IoCompleteRequest(Irp, Boost);
/* If there's a context, dereference it */
if (RxContext != NULL)
{
RxDereferenceAndDeleteRxContext_Real(RxContext);
}
}
VOID
RxCompleteSrvOpenKeyAssociation(
IN OUT PSRV_OPEN SrvOpen)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
NTSTATUS
RxConstructNetRoot(
IN PRX_CONTEXT RxContext,
IN PSRV_CALL SrvCall,
IN PNET_ROOT NetRoot,
IN PV_NET_ROOT VirtualNetRoot,
OUT PLOCK_HOLDING_STATE LockHoldingState)
{
NTSTATUS Status;
PRX_PREFIX_TABLE PrefixTable;
PMRX_CREATENETROOT_CONTEXT Context;
RX_BLOCK_CONDITION RootCondition, VRootCondition;
PAGED_CODE();
DPRINT("RxConstructNetRoot(%p, %p, %p, %p, %p)\n", RxContext, SrvCall, NetRoot,
VirtualNetRoot, LockHoldingState);
/* Validate the lock is exclusively held */
PrefixTable = RxContext->RxDeviceObject->pRxNetNameTable;
ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
/* Allocate the context */
Context = ExAllocatePoolWithTag(PagedPool, sizeof(MRX_CREATENETROOT_CONTEXT), RX_SRVCALL_POOLTAG);
if (Context == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* We can release lock now */
RxReleasePrefixTableLock(PrefixTable);
*LockHoldingState = LHS_LockNotHeld;
RootCondition = Condition_Bad;
VRootCondition = Condition_Bad;
/* Initialize the context */
RtlZeroMemory(Context, sizeof(MRX_CREATENETROOT_CONTEXT));
KeInitializeEvent(&Context->FinishEvent, SynchronizationEvent, FALSE);
Context->RxContext = RxContext;
Context->pVNetRoot = VirtualNetRoot;
Context->Callback = RxCreateNetRootCallBack;
/* And call the mini-rdr */
MINIRDR_CALL_THROUGH(Status, SrvCall->RxDeviceObject->Dispatch, MRxCreateVNetRoot, (Context));
if (Status == STATUS_PENDING)
{
/* Wait for the mini-rdr to be done */
KeWaitForSingleObject(&Context->FinishEvent, Executive, KernelMode, FALSE, NULL);
/* Update the structures condition according to mini-rdr return */
if (NT_SUCCESS(Context->NetRootStatus))
{
if (NT_SUCCESS(Context->VirtualNetRootStatus))
{
RootCondition = Condition_Good;
VRootCondition = Condition_Good;
Status = STATUS_SUCCESS;
}
else
{
RootCondition = Condition_Good;
Status = Context->VirtualNetRootStatus;
}
}
else
{
Status = Context->VirtualNetRootStatus;
if (NT_SUCCESS(Status))
{
Status = Context->NetRootStatus;
}
}
}
else
{
/* It has to return STATUS_PENDING! */
ASSERT(FALSE);
}
/* Acquire lock again - for caller lock status will remain unchanged */
ASSERT(*LockHoldingState == LHS_LockNotHeld);
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
*LockHoldingState = LHS_ExclusiveLockHeld;
/* Do the transition to the condition got from mini-rdr */
RxTransitionNetRoot(NetRoot, RootCondition);
RxTransitionVNetRoot(VirtualNetRoot, VRootCondition);
/* Context is not longer needed */
ExFreePoolWithTag(Context, RX_SRVCALL_POOLTAG);
DPRINT("Status: %x\n", Status);
return Status;
}
/*
* @implemented
*/
NTSTATUS
RxConstructSrvCall(
IN PRX_CONTEXT RxContext,
IN PSRV_CALL SrvCall,
OUT PLOCK_HOLDING_STATE LockHoldingState)
{
NTSTATUS Status;
PRX_PREFIX_TABLE PrefixTable;
PRDBSS_DEVICE_OBJECT RxDeviceObject;
PMRX_SRVCALLDOWN_STRUCTURE Calldown;
PMRX_SRVCALL_CALLBACK_CONTEXT CallbackContext;
PAGED_CODE();
DPRINT("RxConstructSrvCall(%p, %p, %p)\n", RxContext, SrvCall, LockHoldingState);
/* Validate the lock is exclusively held */
RxDeviceObject = RxContext->RxDeviceObject;
PrefixTable = RxDeviceObject->pRxNetNameTable;
ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
/* Allocate the context for mini-rdr */
Calldown = ExAllocatePoolWithTag(NonPagedPool, sizeof(MRX_SRVCALLDOWN_STRUCTURE), RX_SRVCALL_POOLTAG);
if (Calldown == NULL)
{
SrvCall->Context = NULL;
SrvCall->Condition = Condition_Bad;
RxReleasePrefixTableLock(PrefixTable);
*LockHoldingState = LHS_LockNotHeld;
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize it */
RtlZeroMemory(Calldown, sizeof(MRX_SRVCALLDOWN_STRUCTURE));
SrvCall->Context = NULL;
SrvCall->Condition = Condition_InTransition;
RxReleasePrefixTableLock(PrefixTable);
*LockHoldingState = LHS_LockNotHeld;
CallbackContext = &Calldown->CallbackContexts[0];
DPRINT("CalldownContext %p for %wZ\n", CallbackContext, &RxDeviceObject->DeviceName);
DPRINT("With calldown %p and SrvCall %p\n", Calldown, SrvCall);
CallbackContext->SrvCalldownStructure = Calldown;
CallbackContext->CallbackContextOrdinal = 0;
CallbackContext->RxDeviceObject = RxDeviceObject;
RxReferenceSrvCall(SrvCall);
/* If we're async, we'll post, otherwise, we'll have to wait for completion */
if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
{
RxPrePostIrp(RxContext, RxContext->CurrentIrp);
}
else
{
KeInitializeEvent(&Calldown->FinishEvent, SynchronizationEvent, FALSE);
}
Calldown->NumberToWait = 1;
Calldown->NumberRemaining = 1;
Calldown->RxContext = RxContext;
Calldown->SrvCall = (PMRX_SRV_CALL)SrvCall;
Calldown->CallBack = RxCreateSrvCallCallBack;
Calldown->BestFinisher = NULL;
CallbackContext->Status = STATUS_BAD_NETWORK_PATH;
InitializeListHead(&Calldown->SrvCalldownList);
/* Call the mini-rdr */
ASSERT(RxDeviceObject->Dispatch != NULL);
ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
ASSERT(RxDeviceObject->Dispatch->MRxCreateSrvCall != NULL);
Status = RxDeviceObject->Dispatch->MRxCreateSrvCall((PMRX_SRV_CALL)SrvCall, CallbackContext);
/* It has to return STATUS_PENDING! */
ASSERT(Status == STATUS_PENDING);
/* No async, start completion */
if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
{
KeWaitForSingleObject(&Calldown->FinishEvent, Executive, KernelMode, FALSE, NULL);
/* Finish construction - we'll notify mini-rdr it's the winner */
Status = RxFinishSrvCallConstruction(Calldown);
if (!NT_SUCCESS(Status))
{
RxReleasePrefixTableLock(PrefixTable);
*LockHoldingState = LHS_LockNotHeld;
}
else
{
ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
*LockHoldingState = LHS_ExclusiveLockHeld;
}
}
DPRINT("RxConstructSrvCall() = Status: %x\n", Status);
return Status;
}
/*
* @implemented
*/
NTSTATUS
RxConstructVirtualNetRoot(
IN PRX_CONTEXT RxContext,
IN PUNICODE_STRING CanonicalName,
IN NET_ROOT_TYPE NetRootType,
OUT PV_NET_ROOT *VirtualNetRootPointer,
OUT PLOCK_HOLDING_STATE LockHoldingState,
OUT PRX_CONNECTION_ID RxConnectionId)
{
NTSTATUS Status;
PV_NET_ROOT VNetRoot;
RX_BLOCK_CONDITION Condition;
UNICODE_STRING LocalNetRootName, FilePathName;
PAGED_CODE();
ASSERT(*LockHoldingState != LHS_LockNotHeld);
VNetRoot = NULL;
Condition = Condition_Bad;
/* Before creating the VNetRoot, try to find the appropriate connection */
Status = RxFindOrCreateConnections(RxContext, CanonicalName, NetRootType,
&LocalNetRootName, &FilePathName,
LockHoldingState, RxConnectionId);
/* Found and active */
if (Status == STATUS_CONNECTION_ACTIVE)
{
/* We need a new VNetRoot */
VNetRoot = RxCreateVNetRoot(RxContext, (PNET_ROOT)RxContext->Create.pVNetRoot->pNetRoot,
CanonicalName, &LocalNetRootName, &FilePathName, RxConnectionId);
if (VNetRoot != NULL)
{
RxReferenceVNetRoot(VNetRoot);
}
/* Dereference previous VNetRoot */
RxDereferenceVNetRoot(RxContext->Create.pVNetRoot->pNetRoot, *LockHoldingState);
/* Reset and start construct (new structures will replace old ones) */
RxContext->Create.pSrvCall = NULL;
RxContext->Create.pNetRoot = NULL;
RxContext->Create.pVNetRoot = NULL;
/* Construct new NetRoot */
if (VNetRoot != NULL)
{
Status = RxConstructNetRoot(RxContext, (PSRV_CALL)VNetRoot->pNetRoot->pSrvCall,
(PNET_ROOT)VNetRoot->pNetRoot, VNetRoot, LockHoldingState);
if (NT_SUCCESS(Status))
{
Condition = Condition_Good;
}
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
/* If it failed creating the connection, leave */
if (Status != STATUS_SUCCESS)
{
if (*LockHoldingState != LHS_LockNotHeld)
{
RxReleasePrefixTableLock(RxContext->RxDeviceObject->pRxNetNameTable);
*LockHoldingState = LHS_LockNotHeld;
}
*VirtualNetRootPointer = VNetRoot;
DPRINT("RxConstructVirtualNetRoot() = Status: %x\n", Status);
return Status;
}
*LockHoldingState = LHS_ExclusiveLockHeld;
VNetRoot = (PV_NET_ROOT)RxContext->Create.pVNetRoot;
Condition = Condition_Good;
}
/* We have a non stable VNetRoot - transition it */
if (VNetRoot != NULL && !StableCondition(VNetRoot->Condition))
{
RxTransitionVNetRoot(VNetRoot, Condition);
}
/* If recreation failed */
if (Status != STATUS_SUCCESS)
{
/* Dereference potential VNetRoot */
if (VNetRoot != NULL)
{
ASSERT(*LockHoldingState != LHS_LockNotHeld);
RxDereferenceVNetRoot(VNetRoot, *LockHoldingState);
VNetRoot = NULL;
}
/* Release lock */
if (*LockHoldingState != LHS_LockNotHeld)
{
RxReleasePrefixTableLock(RxContext->RxDeviceObject->pRxNetNameTable);
*LockHoldingState = LHS_LockNotHeld;
}
/* Set NULL ptr */
*VirtualNetRootPointer = VNetRoot;
return Status;
}
/* Return the allocated VNetRoot */
*VirtualNetRootPointer = VNetRoot;
return Status;
}
/*
* @implemented
*/
PFCB
RxCreateNetFcb(
IN PRX_CONTEXT RxContext,
IN PV_NET_ROOT VNetRoot,
IN PUNICODE_STRING Name)
{
PFCB Fcb;
BOOLEAN FakeFcb;
PNET_ROOT NetRoot;
POOL_TYPE PoolType;
NODE_TYPE_CODE NodeType;
PIO_STACK_LOCATION Stack;
PRDBSS_DEVICE_OBJECT RxDeviceObject;
PAGED_CODE();
/* We need a decent VNetRoot */
ASSERT(VNetRoot != NULL && NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT);
NetRoot = (PNET_ROOT)VNetRoot->pNetRoot;
ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
ASSERT((PMRX_NET_ROOT)NetRoot == RxContext->Create.pNetRoot);
RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject;
ASSERT(RxDeviceObject == RxContext->RxDeviceObject);
Stack = RxContext->CurrentIrpSp;
/* Do we need to create a fake FCB? Like for renaming */
FakeFcb = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY) &&
!BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_SUPPORTS_SYMBOLIC_LINKS);
ASSERT(FakeFcb || RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
PoolType = (BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE) ? NonPagedPool : PagedPool);
NodeType = (FakeFcb) ? RDBSS_NTC_OPENTARGETDIR_FCB : RDBSS_STORAGE_NTC(FileTypeNotYetKnown);
/* Allocate the FCB */
Fcb = RxAllocateFcbObject(RxDeviceObject, NodeType, PoolType,
NetRoot->InnerNamePrefix.Length + Name->Length, NULL);
if (Fcb == NULL)
{
return NULL;
}
/* Initialize the FCB */
Fcb->CachedNetRootType = NetRoot->Type;
Fcb->RxDeviceObject = RxDeviceObject;
Fcb->MRxDispatch = RxDeviceObject->Dispatch;
Fcb->VNetRoot = VNetRoot;
Fcb->pNetRoot = VNetRoot->pNetRoot;
InitializeListHead(&Fcb->SrvOpenList);
Fcb->SrvOpenListVersion = 0;
Fcb->FcbTableEntry.Path.Length = Name->Length;
Fcb->FcbTableEntry.Path.MaximumLength = Name->Length;
Fcb->FcbTableEntry.Path.Buffer = Add2Ptr(Fcb->PrivateAlreadyPrefixedName.Buffer, NetRoot->InnerNamePrefix.Length);
RtlMoveMemory(Fcb->PrivateAlreadyPrefixedName.Buffer, NetRoot->InnerNamePrefix.Buffer,
NetRoot->InnerNamePrefix.Length);
RtlMoveMemory(Fcb->FcbTableEntry.Path.Buffer, Name->Buffer, Name->Length);
/* Copy back parameters from RxContext */
if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_ADDEDBACKSLASH))
{
Fcb->FcbState |= FCB_STATE_ADDEDBACKSLASH;
}
InitializeListHead(&Fcb->NonPaged->TransitionWaitList);
if (BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE))
{
Fcb->FcbState |= FCB_STATE_PAGING_FILE;
}
if (RxContext->MajorFunction == IRP_MJ_CREATE && BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_SPECIAL_PATH))
{
Fcb->FcbState |= FCB_STATE_SPECIAL_PATH;
}
Fcb->Header.Resource = &Fcb->NonPaged->HeaderResource;
ExInitializeResourceLite(Fcb->Header.Resource);
Fcb->Header.PagingIoResource = &Fcb->NonPaged->PagingIoResource;
ExInitializeResourceLite(Fcb->Header.PagingIoResource);
Fcb->BufferedLocks.Resource = &Fcb->NonPaged->BufferedLocksResource;
ExInitializeResourceLite(Fcb->BufferedLocks.Resource);
/* Fake FCB doesn't go in prefix table */
if (FakeFcb)
{
Fcb->FcbState |= (FCB_STATE_FAKEFCB | FCB_STATE_NAME_ALREADY_REMOVED);
InitializeListHead(&Fcb->FcbTableEntry.HashLinks);
DPRINT("Fake FCB: %p\n", Fcb);
}
else
{
RxFcbTableInsertFcb(&NetRoot->FcbTable, Fcb);
}
RxReferenceVNetRoot(VNetRoot);
InterlockedIncrement((volatile long *)&Fcb->pNetRoot->NumberOfFcbs);
Fcb->ulFileSizeVersion = 0;
DPRINT("FCB %p for %wZ\n", Fcb, &Fcb->FcbTableEntry.Path);
RxReferenceNetFcb(Fcb);
return Fcb;
}
/*
* @implemented
*/
PMRX_FOBX
NTAPI
RxCreateNetFobx(
OUT PRX_CONTEXT RxContext,
IN PMRX_SRV_OPEN MrxSrvOpen)
{
PFCB Fcb;
PFOBX Fobx;
ULONG Flags;
PNET_ROOT NetRoot;
PSRV_OPEN SrvOpen;
POOL_TYPE PoolType;
PAGED_CODE();
SrvOpen = (PSRV_OPEN)MrxSrvOpen;
ASSERT(NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN);
ASSERT(NodeTypeIsFcb(SrvOpen->Fcb));
ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
Fcb = SrvOpen->Fcb;
PoolType = (BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool);
/* Can we use pre-allocated FOBX? */
if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_FOBX_USED) && Fcb->InternalSrvOpen == (PSRV_OPEN)MrxSrvOpen)
{
Fobx = Fcb->InternalFobx;
/* Call allocate to initialize the FOBX */
RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, Fobx);
/* Mark it used now */
Fcb->FcbState |= FCB_STATE_FOBX_USED;
Flags = FOBX_FLAG_ENCLOSED_ALLOCATED;
}
else if (!BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FOBX_USED))
{
Fobx = SrvOpen->InternalFobx;
/* Call allocate to initialize the FOBX */
RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, Fobx);
/* Mark it used now */
SrvOpen->Flags |= SRVOPEN_FLAG_FOBX_USED;
Flags = FOBX_FLAG_ENCLOSED_ALLOCATED;
}
else
{
/* Last case, we cannot, allocate a FOBX */
Fobx = RxAllocateFcbObject(Fcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, NULL);
Flags = 0;
}
/* Allocation failed! */
if (Fobx == NULL)
{
return NULL;
}
/* Set flags */
Fobx->Flags = Flags;
/* Initialize throttling */
NetRoot = (PNET_ROOT)RxContext->Create.pNetRoot;
if (NetRoot != NULL)
{
if (NetRoot->DeviceType == FILE_DEVICE_DISK)
{
RxInitializeThrottlingState(&Fobx->Specific.DiskFile.LockThrottlingState,
NetRoot->DiskParameters.LockThrottlingParameters.Increment,
NetRoot->DiskParameters.LockThrottlingParameters.MaximumDelay);
}
else if (NetRoot->DeviceType == FILE_DEVICE_NAMED_PIPE)
{
RxInitializeThrottlingState(&Fobx->Specific.NamedPipe.ThrottlingState,
NetRoot->NamedPipeParameters.PipeReadThrottlingParameters.Increment,
NetRoot->NamedPipeParameters.PipeReadThrottlingParameters.MaximumDelay);
}
}
/* Propagate flags fron RxContext */
if (BooleanFlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME))
{
Fobx->Flags |= FOBX_FLAG_UNC_NAME;
}
if (BooleanFlagOn(RxContext->Create.NtCreateParameters.CreateOptions, FILE_OPEN_FOR_BACKUP_INTENT))
{
Fobx->Flags |= FOBX_FLAG_BACKUP_INTENT;
}
/* Continue init */
Fobx->FobxSerialNumber = 0;
Fobx->SrvOpen = (PSRV_OPEN)MrxSrvOpen;
Fobx->NodeReferenceCount = 1;
Fobx->RxDeviceObject = Fcb->RxDeviceObject;
RxReferenceSrvOpen(SrvOpen);
InterlockedIncrement((volatile long *)&SrvOpen->pVNetRoot->NumberOfFobxs);
InsertTailList(&SrvOpen->FobxList, &Fobx->FobxQLinks);
InitializeListHead(&Fobx->ScavengerFinalizationList);
InitializeListHead(&Fobx->ClosePendingList);
Fobx->CloseTime.QuadPart = 0;
Fobx->fOpenCountDecremented = FALSE;
DPRINT("FOBX %p for SRV_OPEN %p FCB %p\n", Fobx, Fobx->SrvOpen, Fobx->SrvOpen->pFcb);
return (PMRX_FOBX)Fobx;
}
/*
* @implemented
*/
PNET_ROOT
RxCreateNetRoot(
IN PSRV_CALL SrvCall,
IN PUNICODE_STRING Name,
IN ULONG NetRootFlags,
IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
{
PNET_ROOT NetRoot;
USHORT CaseInsensitiveLength;
PRX_PREFIX_TABLE PrefixTable;
DPRINT("RxCreateNetRoot(%p, %wZ, %x, %p)\n", SrvCall, Name, NetRootFlags, RxConnectionId);
PAGED_CODE();
/* We need a SRV_CALL */
ASSERT(SrvCall != NULL);
PrefixTable = SrvCall->RxDeviceObject->pRxNetNameTable;
ASSERT(RxIsPrefixTableLockExclusive(PrefixTable));
/* Get name length */
CaseInsensitiveLength = SrvCall->PrefixEntry.Prefix.Length + Name->Length;
if (CaseInsensitiveLength > MAXUSHORT)
{
return NULL;
}
/* Allocate the NetRoot */
NetRoot = RxAllocateObject(RDBSS_NTC_NETROOT, SrvCall->RxDeviceObject->Dispatch,
CaseInsensitiveLength);
if (NetRoot == NULL)
{
return NULL;
}
/* Construct name */
RtlMoveMemory(Add2Ptr(NetRoot->PrefixEntry.Prefix.Buffer, SrvCall->PrefixEntry.Prefix.Length),
Name->Buffer, Name->Length);
if (SrvCall->PrefixEntry.Prefix.Length != 0)
{
RtlMoveMemory(NetRoot->PrefixEntry.Prefix.Buffer, SrvCall->PrefixEntry.Prefix.Buffer,
SrvCall->PrefixEntry.Prefix.Length);
}
if (!BooleanFlagOn(SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS))
{
CaseInsensitiveLength = SrvCall->PrefixEntry.CaseInsensitiveLength;
}
/* Inisert in prefix table */
RxPrefixTableInsertName(PrefixTable, &NetRoot->PrefixEntry, NetRoot,
(PULONG)&NetRoot->NodeReferenceCount, CaseInsensitiveLength,
RxConnectionId);
/* Prepare the FCB table */
RxInitializeFcbTable(&NetRoot->FcbTable, TRUE);
InitializeListHead(&NetRoot->TransitionWaitList);
InitializeListHead(&NetRoot->ScavengerFinalizationList);
InitializeListHead(&NetRoot->VirtualNetRoots);
RxInitializePurgeSyncronizationContext(&NetRoot->PurgeSyncronizationContext);
NetRoot->SerialNumberForEnum = SerialNumber++;
NetRoot->Flags |= NetRootFlags;
NetRoot->DiskParameters.ClusterSize = 1;
NetRoot->DiskParameters.ReadAheadGranularity = ReadAheadGranularity;
NetRoot->SrvCall = SrvCall;
RxReferenceSrvCall(SrvCall);
DPRINT("NetRootName: %wZ (%p)\n", NetRoot->pNetRootName, NetRoot);
return NetRoot;
}
/*
* @implemented
*/
VOID
NTAPI
RxCreateNetRootCallBack(
IN PMRX_CREATENETROOT_CONTEXT CreateNetRootContext)
{
PAGED_CODE();
KeSetEvent(&CreateNetRootContext->FinishEvent, IO_NETWORK_INCREMENT, FALSE);
}
/*
* @implemented
*/
PRX_CONTEXT
NTAPI
RxCreateRxContext(
IN PIRP Irp,
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN ULONG InitialContextFlags)
{
KIRQL OldIrql;
PRX_CONTEXT Context;
ASSERT(RxDeviceObject != NULL);
DPRINT("RxCreateRxContext(%p, %p, %u)\n", Irp, RxDeviceObject, InitialContextFlags);
InterlockedIncrement((volatile LONG *)&RxFsdEntryCount);
InterlockedIncrement((volatile LONG *)&RxDeviceObject->NumberOfActiveContexts);
/* Allocate the context from our lookaside list */
Context = ExAllocateFromNPagedLookasideList(&RxContextLookasideList);
if (Context == NULL)
{
return NULL;
}
/* And initialize it */
RtlZeroMemory(Context, sizeof(RX_CONTEXT));
RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, Context);
ASSERT((Context->MajorFunction != IRP_MJ_CREATE) || !BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED));
/* Add it to our global list */
KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
InsertTailList(&RxActiveContexts, &Context->ContextListEntry);
KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
DPRINT("Context: %p\n", Context);
return Context;
}
/*
* @implemented
*/
PSRV_CALL
RxCreateSrvCall(
IN PRX_CONTEXT RxContext,
IN PUNICODE_STRING Name,
IN PUNICODE_STRING InnerNamePrefix OPTIONAL,
IN PRX_CONNECTION_ID RxConnectionId)
{
ULONG NameLength;
PSRV_CALL SrvCall;
PAGED_CODE();
DPRINT("RxCreateSrvCall(%p, %wZ, %wZ, %p)\n", RxContext, Name, InnerNamePrefix, RxConnectionId);
ASSERT(RxIsPrefixTableLockExclusive(RxContext->RxDeviceObject->pRxNetNameTable));
/* Get the name length */
NameLength = Name->Length + 2 * sizeof(WCHAR);
if (InnerNamePrefix != NULL)
{
NameLength += InnerNamePrefix->Length;
}
/* Allocate the object */
SrvCall = RxAllocateObject(RDBSS_NTC_SRVCALL, NULL, NameLength);
if (SrvCall == NULL)
{
return NULL;
}
/* Initialize it */
SrvCall->SerialNumberForEnum = SerialNumber++;
SrvCall->RxDeviceObject = RxContext->RxDeviceObject;
RxInitializeBufferingManager(SrvCall);
InitializeListHead(&SrvCall->TransitionWaitList);
InitializeListHead(&SrvCall->ScavengerFinalizationList);
RxInitializePurgeSyncronizationContext(&SrvCall->PurgeSyncronizationContext);
RxInitializeSrvCallParameters(RxContext, SrvCall);
RtlMoveMemory(SrvCall->PrefixEntry.Prefix.Buffer, Name->Buffer, Name->Length);
SrvCall->PrefixEntry.Prefix.MaximumLength = Name->Length + 2 * sizeof(WCHAR);
SrvCall->PrefixEntry.Prefix.Length = Name->Length;
RxPrefixTableInsertName(RxContext->RxDeviceObject->pRxNetNameTable, &SrvCall->PrefixEntry,
SrvCall, (PULONG)&SrvCall->NodeReferenceCount, Name->Length, RxConnectionId);
DPRINT("SrvCallName: %wZ (%p)\n", SrvCall->pSrvCallName, SrvCall);
return SrvCall;
}
/*
* @implemented
*/
VOID
NTAPI
RxCreateSrvCallCallBack(
IN OUT PMRX_SRVCALL_CALLBACK_CONTEXT Context)
{
KIRQL OldIrql;
PSRV_CALL SrvCall;
PRX_CONTEXT RxContext;
ULONG NumberRemaining;
BOOLEAN StartDispatcher;
PMRX_SRVCALLDOWN_STRUCTURE Calldown;
DPRINT("RxCreateSrvCallCallBack(%p)\n", Context);
/* Get our context structures */
Calldown = Context->SrvCalldownStructure;
SrvCall = (PSRV_CALL)Calldown->SrvCall;
/* If it is a success, that's the winner */
KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
if (Context->Status == STATUS_SUCCESS)
{
Calldown->BestFinisherOrdinal = Context->CallbackContextOrdinal;
Calldown->BestFinisher = Context->RxDeviceObject;
}
NumberRemaining = --Calldown->NumberRemaining;
SrvCall->Status = Context->Status;
KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
/* Still some to ask, keep going */
if (NumberRemaining != 0)
{
return;
}
/* If that's not async, signal we're done */
RxContext = Calldown->RxContext;
if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
{
KeSetEvent(&Calldown->FinishEvent, IO_NETWORK_INCREMENT, FALSE);
return;
}
/* If that's a mailslot, finish construction, no more to do */
else if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CREATE_MAILSLOT))
{
RxFinishSrvCallConstruction(Calldown);
return;
}
/* Queue our finish call for delayed completion */
DPRINT("Queuing RxFinishSrvCallConstruction() call\n");
KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
InsertTailList(&RxSrvCalldownList, &Calldown->SrvCalldownList);
StartDispatcher = !RxSrvCallConstructionDispatcherActive;
KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
/* If we have to start dispatcher, go ahead */
if (StartDispatcher)
{
NTSTATUS Status;
Status = RxDispatchToWorkerThread(RxFileSystemDeviceObject, CriticalWorkQueue,
RxFinishSrvCallConstructionDispatcher, &RxSrvCalldownList);
if (!NT_SUCCESS(Status))
{
/* It failed - run it manually.... */
RxFinishSrvCallConstructionDispatcher(NULL);
}
}
}
/*
* @implemented
*/
PSRV_OPEN
RxCreateSrvOpen(
IN PV_NET_ROOT VNetRoot,
IN OUT PFCB Fcb)
{
ULONG Flags;
PSRV_OPEN SrvOpen;
POOL_TYPE PoolType;
PAGED_CODE();
ASSERT(NodeTypeIsFcb(Fcb));
ASSERT(RxIsFcbAcquiredExclusive(Fcb));
PoolType = (BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool);
_SEH2_TRY
{
SrvOpen = Fcb->InternalSrvOpen;
/* Check whethet we have to allocate a new SRV_OPEN */
if (Fcb->InternalSrvOpen == NULL || BooleanFlagOn(Fcb->FcbState, FCB_STATE_SRVOPEN_USED) ||
BooleanFlagOn(Fcb->InternalSrvOpen->Flags, SRVOPEN_FLAG_ENCLOSED_ALLOCATED) ||
!IsListEmpty(&Fcb->InternalSrvOpen->SrvOpenQLinks))
{
/* Proceed */
SrvOpen = RxAllocateFcbObject(Fcb->VNetRoot->NetRoot->pSrvCall->RxDeviceObject,
RDBSS_NTC_SRVOPEN, PoolType, 0, NULL);
Flags = 0;
}
else
{
/* Otherwise, just use internal one and initialize it */
RxAllocateFcbObject(Fcb->VNetRoot->NetRoot->pSrvCall->RxDeviceObject,
RDBSS_NTC_INTERNAL_SRVOPEN, PoolType, 0,
Fcb->InternalSrvOpen);
Fcb->FcbState |= FCB_STATE_SRVOPEN_USED;
Flags = SRVOPEN_FLAG_ENCLOSED_ALLOCATED | SRVOPEN_FLAG_FOBX_USED;
}
/* If SrvOpen was properly allocated, initialize it */
if (SrvOpen != NULL)
{
SrvOpen->Flags = Flags;
SrvOpen->pFcb = RX_GET_MRX_FCB(Fcb);
SrvOpen->pAlreadyPrefixedName = &Fcb->PrivateAlreadyPrefixedName;
SrvOpen->pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
SrvOpen->ulFileSizeVersion = Fcb->ulFileSizeVersion;
SrvOpen->NodeReferenceCount = 1;
RxReferenceVNetRoot(VNetRoot);
RxReferenceNetFcb(Fcb);
InsertTailList(&Fcb->SrvOpenList, &SrvOpen->SrvOpenQLinks);
++Fcb->SrvOpenListVersion;
InitializeListHead(&SrvOpen->ScavengerFinalizationList);
InitializeListHead(&SrvOpen->TransitionWaitList);
InitializeListHead(&SrvOpen->FobxList);
InitializeListHead(&SrvOpen->SrvOpenKeyList);
}
}
_SEH2_FINALLY
{
if (_SEH2_AbnormalTermination())
{
if (SrvOpen != NULL)
{
RxFinalizeSrvOpen(SrvOpen, TRUE, TRUE);
SrvOpen = NULL;
}
}
else
{
DPRINT("SrvOpen %p for FCB %p\n", SrvOpen, SrvOpen->pFcb);
}
}
_SEH2_END;
return SrvOpen;
}
/*
* @implemented
*/
PV_NET_ROOT
RxCreateVNetRoot(
IN PRX_CONTEXT RxContext,
IN PNET_ROOT NetRoot,
IN PUNICODE_STRING CanonicalName,
IN PUNICODE_STRING LocalNetRootName,
IN PUNICODE_STRING FilePath,
IN PRX_CONNECTION_ID RxConnectionId)
{
NTSTATUS Status;
PV_NET_ROOT VNetRoot;
USHORT CaseInsensitiveLength;
PAGED_CODE();
DPRINT("RxCreateVNetRoot(%p, %p, %wZ, %wZ, %wZ, %p)\n", RxContext, NetRoot, CanonicalName,
LocalNetRootName, FilePath, RxConnectionId);
/* Lock must be held exclusively */
ASSERT(RxIsPrefixTableLockExclusive(RxContext->RxDeviceObject->pRxNetNameTable));
/* Check for overflow */
if (LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length > MAXUSHORT)
{
return NULL;
}
/* Get name length and allocate VNetRoot */
CaseInsensitiveLength = LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length;
VNetRoot = RxAllocateObject(RDBSS_NTC_V_NETROOT, NetRoot->SrvCall->RxDeviceObject->Dispatch,
CaseInsensitiveLength);
if (VNetRoot == NULL)
{
return NULL;
}
/* Initialize its connection parameters */
Status = RxInitializeVNetRootParameters(RxContext, &VNetRoot->LogonId, &VNetRoot->SessionId,
&VNetRoot->pUserName, &VNetRoot->pUserDomainName,
&VNetRoot->pPassword, &VNetRoot->Flags);
if (!NT_SUCCESS(Status))
{
RxUninitializeVNetRootParameters(VNetRoot->pUserName, VNetRoot->pUserDomainName,
VNetRoot->pPassword, &VNetRoot->Flags);
RxFreeObject(VNetRoot);
return NULL;
}
/* Set name */
RtlMoveMemory(VNetRoot->PrefixEntry.Prefix.Buffer, CanonicalName->Buffer, VNetRoot->PrefixEntry.Prefix.Length);
VNetRoot->PrefixOffsetInBytes = LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length;
VNetRoot->NamePrefix.Buffer = Add2Ptr(VNetRoot->PrefixEntry.Prefix.Buffer, VNetRoot->PrefixOffsetInBytes);
VNetRoot->NamePrefix.Length = VNetRoot->PrefixEntry.Prefix.Length - VNetRoot->PrefixOffsetInBytes;
VNetRoot->NamePrefix.MaximumLength = VNetRoot->PrefixEntry.Prefix.Length - VNetRoot->PrefixOffsetInBytes;
InitializeListHead(&VNetRoot->TransitionWaitList);
InitializeListHead(&VNetRoot->ScavengerFinalizationList);
if (!BooleanFlagOn(NetRoot->SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_FILENAMES))
{
USHORT i;
if (BooleanFlagOn(NetRoot->SrvCall->Flags, SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS))
{
CaseInsensitiveLength = NetRoot->PrefixEntry.CaseInsensitiveLength;
}
else
{
CaseInsensitiveLength = NetRoot->SrvCall->PrefixEntry.CaseInsensitiveLength;
}
for (i = 1; i < CanonicalName->Length / sizeof(WCHAR); ++i)
{
if (CanonicalName->Buffer[i] != OBJ_NAME_PATH_SEPARATOR)
{
break;
}
}
CaseInsensitiveLength += (i * sizeof(WCHAR));
}
/* Insert in prefix table */
RxPrefixTableInsertName(RxContext->RxDeviceObject->pRxNetNameTable, &VNetRoot->PrefixEntry,
VNetRoot, (PULONG)&VNetRoot->NodeReferenceCount, CaseInsensitiveLength,
RxConnectionId);
RxReferenceNetRoot(NetRoot);
RxAddVirtualNetRootToNetRoot(NetRoot, VNetRoot);
/* Finish init */
VNetRoot->SerialNumberForEnum = SerialNumber++;
VNetRoot->UpperFinalizationDone = FALSE;
VNetRoot->ConnectionFinalizationDone = FALSE;
VNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0;
DPRINT("NamePrefix: %wZ\n", &VNetRoot->NamePrefix);
DPRINT("PrefixEntry: %wZ\n", &VNetRoot->PrefixEntry.Prefix);
return VNetRoot;
}
VOID
RxDereference(
IN OUT PVOID Instance,
IN LOCK_HOLDING_STATE LockHoldingState)
{
LONG RefCount;
NODE_TYPE_CODE NodeType;
PNODE_TYPE_AND_SIZE Node;
PAGED_CODE();
RxAcquireScavengerMutex();
/* Check we have a node we can handle */
NodeType = NodeType(Instance);
ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
(NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
(NodeType == RDBSS_NTC_FOBX));
Node = (PNODE_TYPE_AND_SIZE)Instance;
RefCount = InterlockedDecrement((volatile long *)&Node->NodeReferenceCount);
ASSERT(RefCount >= 0);
/* Trace refcount */
switch (NodeType)
{
case RDBSS_NTC_SRVCALL:
PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
break;
case RDBSS_NTC_NETROOT:
PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
break;
case RDBSS_NTC_V_NETROOT:
PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
break;
case RDBSS_NTC_SRVOPEN:
PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
break;
case RDBSS_NTC_FOBX:
PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
break;
default:
ASSERT(FALSE);
break;
}
/* No need to free - still in use */
if (RefCount > 1)
{
RxReleaseScavengerMutex();
return;
}
/* We have to be locked exclusively */
if (LockHoldingState != LHS_ExclusiveLockHeld)
{
UNIMPLEMENTED;
RxReleaseScavengerMutex();
return;
}
RxReleaseScavengerMutex();
/* TODO: Really deallocate stuff - we're leaking as hell! */
switch (NodeType)
{
case RDBSS_NTC_SRVCALL:
{
PSRV_CALL SrvCall;
SrvCall = (PSRV_CALL)Instance;
ASSERT(SrvCall->RxDeviceObject != NULL);
ASSERT(RxIsPrefixTableLockAcquired(SrvCall->RxDeviceObject->pRxNetNameTable));
RxFinalizeSrvCall(SrvCall, TRUE, TRUE);
break;
}
case RDBSS_NTC_NETROOT:
UNIMPLEMENTED;
break;
case RDBSS_NTC_V_NETROOT:
UNIMPLEMENTED;
break;
case RDBSS_NTC_SRVOPEN:
UNIMPLEMENTED;
break;
case RDBSS_NTC_FOBX:
UNIMPLEMENTED;
break;
}
}
/*
* @implemented
*/
VOID
NTAPI
RxDereferenceAndDeleteRxContext_Real(
IN PRX_CONTEXT RxContext)
{
KIRQL OldIrql;
ULONG RefCount;
BOOLEAN Allocated;
PRX_CONTEXT StopContext = NULL;
/* Make sure we really have a context */
KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
ASSERT(RxContext->NodeTypeCode == RDBSS_NTC_RX_CONTEXT);
RefCount = InterlockedDecrement((volatile LONG *)&RxContext->ReferenceCount);
/* If refcount is 0, start releasing stuff that needs spinlock held */
if (RefCount == 0)
{
PRDBSS_DEVICE_OBJECT RxDeviceObject;
Allocated = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_FROM_POOL);
/* If that's stop context from DO, remove it */
RxDeviceObject = RxContext->RxDeviceObject;
if (RxDeviceObject->StartStopContext.pStopContext == RxContext)
{
RxDeviceObject->StartStopContext.pStopContext = NULL;
}
else
{
/* Remove it from the list */
ASSERT((RxContext->ContextListEntry.Flink->Blink == &RxContext->ContextListEntry) &&
(RxContext->ContextListEntry.Blink->Flink == &RxContext->ContextListEntry));
RemoveEntryList(&RxContext->ContextListEntry);
/* If that was the last active context, save the stop context */
if (InterlockedExchangeAdd((volatile LONG *)&RxDeviceObject->NumberOfActiveContexts, -1) == 0)
{
if (RxDeviceObject->StartStopContext.pStopContext != NULL)
{
StopContext = RxDeviceObject->StartStopContext.pStopContext;
}
}
}
}
KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
/* Now, deal with what can be done without spinlock held */
if (RefCount == 0)
{
/* Refcount shouldn't have changed */
ASSERT(RxContext->ReferenceCount == 0);
/* Reset everything that can be */
RxPrepareContextForReuse(RxContext);
#ifdef RDBSS_TRACKER
ASSERT(RxContext->AcquireReleaseFcbTrackerX == 0);
#endif
/* If that was the last active, set the event */
if (StopContext != NULL)
{
StopContext->Flags &= ~RX_CONTEXT_FLAG_RECURSIVE_CALL;
KeSetEvent(&StopContext->SyncEvent, IO_NO_INCREMENT, FALSE);
}
/* Is ShadowCrit still owned? Shouldn't happen! */
if (RxContext->ShadowCritOwner != 0)
{
DPRINT1("ShadowCritOwner not null! %p\n", (PVOID)RxContext->ShadowCritOwner);
ASSERT(FALSE);
}
/* If it was allocated, free it */
if (Allocated)
{
ExFreeToNPagedLookasideList(&RxContextLookasideList, RxContext);
}
}
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RxDispatchToWorkerThread(
IN PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
IN WORK_QUEUE_TYPE WorkQueueType,
IN PRX_WORKERTHREAD_ROUTINE Routine,
IN PVOID pContext)
{
NTSTATUS Status;
PRX_WORK_DISPATCH_ITEM DispatchItem;
/* Allocate a bit of context */
DispatchItem = ExAllocatePoolWithTag(PagedPool, sizeof(RX_WORK_DISPATCH_ITEM), RX_WORKQ_POOLTAG);
if (DispatchItem == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Set all the routines, the one our dispatcher will call, the one ntoskrnl will call */
DispatchItem->DispatchRoutine = Routine;
DispatchItem->DispatchRoutineParameter = pContext;
DispatchItem->WorkQueueItem.WorkerRoutine = RxWorkItemDispatcher;
DispatchItem->WorkQueueItem.Parameter = DispatchItem;
/* Insert item */
Status = RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, DispatchItem);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
DPRINT1("RxInsertWorkQueueItem failed! Queue: %ld, Routine: %p, Context: %p, Status: %lx\n", WorkQueueType, Routine, pContext, Status);
}
DPRINT("Dispatching: %p, %p\n", Routine, pContext);
return Status;
}
/*
* @implemented
*/
VOID
RxExclusivePrefixTableLockToShared(
PRX_PREFIX_TABLE Table)
{
PAGED_CODE();
ExConvertExclusiveToSharedLite(&Table->TableLock);
}
/*
* @implemented
*/
VOID
RxExtractServerName(
IN PUNICODE_STRING FilePathName,
OUT PUNICODE_STRING SrvCallName,
OUT PUNICODE_STRING RestOfName)
{
USHORT i, Length;
PAGED_CODE();
ASSERT(SrvCallName != NULL);
/* SrvCall name will start from the begin up to the first separator */
SrvCallName->Buffer = FilePathName->Buffer;
for (i = 1; i < FilePathName->Length / sizeof(WCHAR); ++i)
{
if (FilePathName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR)
{
break;
}
}
/* Compute length */
Length = (USHORT)((ULONG_PTR)&FilePathName->Buffer[i] - (ULONG_PTR)FilePathName->Buffer);
SrvCallName->MaximumLength = Length;
SrvCallName->Length = Length;
/* Return the rest if asked */
if (RestOfName != NULL)
{
Length = (USHORT)((ULONG_PTR)&FilePathName->Buffer[FilePathName->Length / sizeof(WCHAR)] - (ULONG_PTR)FilePathName->Buffer[i]);
RestOfName->Buffer = &FilePathName->Buffer[i];
RestOfName->MaximumLength = Length;
RestOfName->Length = Length;
}
}
/*
* @implemented
*/
NTSTATUS
RxFcbTableInsertFcb(
IN OUT PRX_FCB_TABLE FcbTable,
IN OUT PFCB Fcb)
{
PAGED_CODE();
/* We deal with the table, make sure it's locked */
ASSERT(RxIsFcbTableLockExclusive(FcbTable));
/* Compute the hash */
Fcb->FcbTableEntry.HashValue = RxTableComputePathHashValue(&Fcb->FcbTableEntry.Path);
RxReferenceNetFcb(Fcb);
/* If no length, it will be our null entry */
if (Fcb->FcbTableEntry.Path.Length == 0)
{
FcbTable->TableEntryForNull = &Fcb->FcbTableEntry;
}
/* Otherwise, insert in the appropriate bucket */
else
{
InsertTailList(FCB_HASH_BUCKET(FcbTable, Fcb->FcbTableEntry.HashValue),
&Fcb->FcbTableEntry.HashLinks);
}
/* Propagate the change by incrementing the version number */
InterlockedIncrement((volatile long *)&FcbTable->Version);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
PFCB
RxFcbTableLookupFcb(
IN PRX_FCB_TABLE FcbTable,
IN PUNICODE_STRING Path)
{
PFCB Fcb;
PRX_FCB_TABLE_ENTRY TableEntry;
PAGED_CODE();
/* No path - easy, that's null entry */
if (Path == NULL)
{
TableEntry = FcbTable->TableEntryForNull;
}
else
{
ULONG Hash;
PLIST_ENTRY HashBucket, ListEntry;
/* Otherwise, compute the hash value and find the associated bucket */
Hash = RxTableComputePathHashValue(Path);
HashBucket = FCB_HASH_BUCKET(FcbTable, Hash);
/* If the bucket is empty, it means there's no entry yet */
if (IsListEmpty(HashBucket))
{
TableEntry = NULL;
}
else
{
/* Otherwise, browse all the entry */
for (ListEntry = HashBucket->Flink;
ListEntry != HashBucket;
ListEntry = ListEntry->Flink)
{
TableEntry = CONTAINING_RECORD(ListEntry, RX_FCB_TABLE_ENTRY, HashLinks);
InterlockedIncrement(&FcbTable->Compares);
/* If entry hash and string are equal, thatt's the one! */
if (TableEntry->HashValue == Hash &&
TableEntry->Path.Length == Path->Length &&
RtlEqualUnicodeString(Path, &TableEntry->Path, FcbTable->CaseInsensitiveMatch))
{
break;
}
}
/* We reached the end? Not found */
if (ListEntry == HashBucket)
{
TableEntry = NULL;
}
}
}
InterlockedIncrement(&FcbTable->Lookups);
/* If table entry isn't null, return the FCB */
if (TableEntry != NULL)
{
Fcb = CONTAINING_RECORD(TableEntry, FCB, FcbTableEntry);
RxReferenceNetFcb(Fcb);
}
else
{
Fcb = NULL;
InterlockedIncrement(&FcbTable->FailedLookups);
}
return Fcb;
}
/*
* @implemented
*/
NTSTATUS
RxFcbTableRemoveFcb(
IN OUT PRX_FCB_TABLE FcbTable,
IN OUT PFCB Fcb)
{
PAGED_CODE();
ASSERT(RxIsPrefixTableLockExclusive(FcbTable));
/* If no path, then remove entry for null */
if (Fcb->FcbTableEntry.Path.Length == 0)
{
FcbTable->TableEntryForNull = NULL;
}
/* Otherwise, remove from the bucket */
else
{
RemoveEntryList(&Fcb->FcbTableEntry.HashLinks);
}
/* Reset its list entry */
InitializeListHead(&Fcb->FcbTableEntry.HashLinks);
/* Propagate the change by incrementing the version number */
InterlockedIncrement((volatile long *)&FcbTable->Version);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
BOOLEAN
RxFinalizeNetFcb(
OUT PFCB ThisFcb,
IN BOOLEAN RecursiveFinalize,
IN BOOLEAN ForceFinalize,
IN LONG ReferenceCount)
{
PAGED_CODE();
DPRINT("RxFinalizeNetFcb(%p, %d, %d, %d)\n", ThisFcb, RecursiveFinalize, ForceFinalize, ReferenceCount);
DPRINT("Finalize: %wZ\n", &ThisFcb->FcbTableEntry.Path);
/* Make sure we have an exclusively acquired FCB */
ASSERT_CORRECT_FCB_STRUCTURE(ThisFcb);
ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
/* We shouldn't force finalization... */
ASSERT(!ForceFinalize);
/* If recurisve, finalize all the associated SRV_OPEN */
if (RecursiveFinalize)
{
PLIST_ENTRY ListEntry;
for (ListEntry = ThisFcb->SrvOpenList.Flink;
ListEntry != &ThisFcb->SrvOpenList;
ListEntry = ListEntry->Flink)
{
PSRV_OPEN SrvOpen;
SrvOpen = CONTAINING_RECORD(ListEntry, SRV_OPEN, SrvOpenQLinks);
RxFinalizeSrvOpen(SrvOpen, TRUE, ForceFinalize);
}
}
/* If FCB is still in use, that's over */
else
{
if (ThisFcb->OpenCount != 0 || ThisFcb->UncleanCount != 0)
{
ASSERT(ReferenceCount > 0);
return FALSE;
}
}
ASSERT(ReferenceCount >= 1);
/* If FCB is still referenced, that's over - unless you force it and want to BSOD somewhere */
if (ReferenceCount != 1 && !ForceFinalize)
{
return FALSE;
}
ASSERT(ForceFinalize || ((ThisFcb->OpenCount == 0) && (ThisFcb->UncleanCount == 0)));
DPRINT("Finalizing FCB open: %d (%d)", ThisFcb->OpenCount, ForceFinalize);
/* If finalization was not already initiated, go ahead */
if (!ThisFcb->UpperFinalizationDone)
{
/* Free any FCB_LOCK */
if (NodeType(ThisFcb) == RDBSS_NTC_STORAGE_TYPE_FILE)
{
FsRtlUninitializeFileLock(&ThisFcb->Specific.Fcb.FileLock);
while (ThisFcb->BufferedLocks.List != NULL)
{
PFCB_LOCK Entry;
Entry = ThisFcb->BufferedLocks.List;
ThisFcb->BufferedLocks.List = Entry->Next;
ExFreePool(Entry);
}
}
/* If not orphaned, it still has a NET_ROOT and potentially is still in a table */
if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_ORPHANED))
{
PNET_ROOT NetRoot;
NetRoot = (PNET_ROOT)ThisFcb->pNetRoot;
ASSERT(RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
/* So, remove it */
if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED))
{
RxFcbTableRemoveFcb(&NetRoot->FcbTable, ThisFcb);
}
}
ThisFcb->UpperFinalizationDone = TRUE;
}
ASSERT(ReferenceCount >= 1);
/* Even if forced, don't allow broken free */
if (ReferenceCount != 1)
{
return FALSE;
}
/* Now, release everything */
if (ThisFcb->pBufferingStateChangeCompletedEvent != NULL)
{
ExFreePool(ThisFcb->pBufferingStateChangeCompletedEvent);
}
if (ThisFcb->MRxDispatch != NULL)
{
ThisFcb->MRxDispatch->MRxDeallocateForFcb(RX_GET_MRX_FCB(ThisFcb));
}
ExDeleteResourceLite(ThisFcb->BufferedLocks.Resource);
ExDeleteResourceLite(ThisFcb->Header.Resource);
ExDeleteResourceLite(ThisFcb->Header.PagingIoResource);
InterlockedDecrement((volatile long *)&ThisFcb->pNetRoot->NumberOfFcbs);
RxDereferenceNetRoot(ThisFcb->pNetRoot, LHS_LockNotHeld);
ASSERT(IsListEmpty(&ThisFcb->FcbTableEntry.HashLinks));
ASSERT(!ThisFcb->fMiniInited);
/* And free the object */
RxFreeFcbObject(ThisFcb);
return TRUE;
}
BOOLEAN
RxFinalizeNetRoot(
OUT PNET_ROOT ThisNetRoot,
IN BOOLEAN RecursiveFinalize,
IN BOOLEAN ForceFinalize
)
{
UNIMPLEMENTED;
return FALSE;
}
BOOLEAN
RxFinalizeSrvCall(
OUT PSRV_CALL ThisSrvCall,
IN BOOLEAN RecursiveFinalize,
IN BOOLEAN ForceFinalize)
{
UNIMPLEMENTED;
return FALSE;
}
BOOLEAN
RxFinalizeSrvOpen(
OUT PSRV_OPEN ThisSrvOpen,
IN BOOLEAN RecursiveFinalize,
IN BOOLEAN ForceFinalize)
{
UNIMPLEMENTED;
return FALSE;
}
NTSTATUS
RxFindOrConstructVirtualNetRoot(
IN PRX_CONTEXT RxContext,
IN PUNICODE_STRING CanonicalName,
IN NET_ROOT_TYPE NetRootType,
IN PUNICODE_STRING RemainingName)
{
ULONG Flags;
NTSTATUS Status;
PVOID Container;
BOOLEAN Construct;
PV_NET_ROOT VNetRoot;
RX_CONNECTION_ID ConnectionID;
PRDBSS_DEVICE_OBJECT RxDeviceObject;
LOCK_HOLDING_STATE LockHoldingState;
PAGED_CODE();
RxDeviceObject = RxContext->RxDeviceObject;
ASSERT(RxDeviceObject->Dispatch != NULL);
ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
/* Ask the mini-rdr for connection ID */
ConnectionID.SessionID = 0;
if (RxDeviceObject->Dispatch->MRxGetConnectionId != NULL)
{
Status = RxDeviceObject->Dispatch->MRxGetConnectionId(RxContext, &ConnectionID);
if (!NT_SUCCESS(Status) && Status != STATUS_NOT_IMPLEMENTED)
{
/* mini-rdr is expected not to fail - unless it's not implemented */
DPRINT1("Failed to initialize connection ID\n");
ASSERT(FALSE);
}
}
RxContext->Create.NetNamePrefixEntry = NULL;
Status = STATUS_MORE_PROCESSING_REQUIRED;
RxAcquirePrefixTableLockShared(RxDeviceObject->pRxNetNameTable, TRUE);
LockHoldingState = LHS_SharedLockHeld;
Construct = TRUE;
Flags = 0;
/* We will try twice to find a matching VNetRoot: shared locked and then exlusively locked */
while (TRUE)
{
PNET_ROOT NetRoot;
PV_NET_ROOT SavedVNetRoot;
/* Look in prefix table */
Container = RxPrefixTableLookupName(RxDeviceObject->pRxNetNameTable, CanonicalName, RemainingName, &ConnectionID);
if (Container != NULL)
{
/* If that's not a VNetRoot, that's a SrvCall, not interesting, loop again */
if (NodeType(Container) != RDBSS_NTC_V_NETROOT)
{
ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
RxDereferenceSrvCall(Container, LockHoldingState);
}
else
{
VNetRoot = Container;
NetRoot = VNetRoot->NetRoot;
/* If the matching VNetRoot isn't in a good shape, there's something wrong - fail */
if ((NetRoot->Condition != Condition_InTransition && NetRoot->Condition != Condition_Good) ||
NetRoot->SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
{
Status = STATUS_BAD_NETWORK_PATH;
SavedVNetRoot = NULL;
}
else
{
LUID LogonId;
ULONG SessionId;
PUNICODE_STRING UserName, UserDomain, Password;
/* We can reuse if we use same credentials */
Status = RxInitializeVNetRootParameters(RxContext, &LogonId,
&SessionId, &UserName,
&UserDomain, &Password,
&Flags);
if (NT_SUCCESS(Status))
{
SavedVNetRoot = VNetRoot;
Status = RxCheckVNetRootCredentials(RxContext, VNetRoot,
&LogonId, UserName,
UserDomain, Password,
Flags);
if (Status == STATUS_MORE_PROCESSING_REQUIRED)
{
PLIST_ENTRY ListEntry;
for (ListEntry = NetRoot->VirtualNetRoots.Flink;
ListEntry != &NetRoot->VirtualNetRoots;
ListEntry = ListEntry->Flink)
{
SavedVNetRoot = CONTAINING_RECORD(ListEntry, V_NET_ROOT, NetRootListEntry);
Status = RxCheckVNetRootCredentials(RxContext, SavedVNetRoot,
&LogonId, UserName,
UserDomain, Password,
Flags);
if (Status != STATUS_MORE_PROCESSING_REQUIRED)
{
break;
}
}
if (ListEntry == &NetRoot->VirtualNetRoots)
{
SavedVNetRoot = NULL;
}
}
if (!NT_SUCCESS(Status))
{
SavedVNetRoot = NULL;
}
RxUninitializeVNetRootParameters(UserName, UserDomain, Password, &Flags);
}
}
/* We'll fail, if we had referenced a VNetRoot, dereference it */
if (Status != STATUS_MORE_PROCESSING_REQUIRED && !NT_SUCCESS(Status))
{
if (SavedVNetRoot == NULL)
{
RxDereferenceVNetRoot(VNetRoot, LockHoldingState);
}
}
/* Reference VNetRoot we'll keep, and dereference current */
else if (SavedVNetRoot != VNetRoot)
{
RxDereferenceVNetRoot(VNetRoot, LockHoldingState);
if (SavedVNetRoot != NULL)
{
RxReferenceVNetRoot(SavedVNetRoot);
}
}
}
/* We may have found something, or we fail hard, so don't attempt to create a VNetRoot */
if (Status != STATUS_MORE_PROCESSING_REQUIRED)
{
Construct = FALSE;
break;
}
}
/* If we're locked exclusive, we won't loop again, it was the second pass */
if (LockHoldingState != LHS_SharedLockHeld)
{
break;
}
/* Otherwise, prepare for second pass, exclusive, making sure we can acquire without delay */
if (RxAcquirePrefixTableLockExclusive(RxDeviceObject->pRxNetNameTable, FALSE))
{
RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
LockHoldingState = LHS_ExclusiveLockHeld;
break;
}
RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
RxAcquirePrefixTableLockExclusive(RxDeviceObject->pRxNetNameTable, TRUE);
LockHoldingState = LHS_ExclusiveLockHeld;
}
/* We didn't fail, and didn't find any VNetRoot, construct one */
if (Construct)
{
ASSERT(LockHoldingState == LHS_ExclusiveLockHeld);
Status = RxConstructVirtualNetRoot(RxContext, CanonicalName, NetRootType, &VNetRoot, &LockHoldingState, &ConnectionID);
ASSERT(Status != STATUS_SUCCESS || LockHoldingState != LHS_LockNotHeld);
if (Status == STATUS_SUCCESS)
{
DPRINT("CanonicalName: %wZ (%d)\n", CanonicalName, CanonicalName->Length);
DPRINT("VNetRoot: %wZ (%d)\n", &VNetRoot->PrefixEntry.Prefix, VNetRoot->PrefixEntry.Prefix.Length);
ASSERT(CanonicalName->Length >= VNetRoot->PrefixEntry.Prefix.Length);
RemainingName->Buffer = Add2Ptr(CanonicalName->Buffer, VNetRoot->PrefixEntry.Prefix.Length);
RemainingName->Length = CanonicalName->Length - VNetRoot->PrefixEntry.Prefix.Length;
RemainingName->MaximumLength = RemainingName->Length;
if (BooleanFlagOn(Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE))
{
DPRINT("CSC instance, VNetRoot: %p\n", VNetRoot);
}
VNetRoot->Flags |= Flags;
}
}
/* Release the prefix table - caller expects it to be released */
if (LockHoldingState != LHS_LockNotHeld)
{
RxReleasePrefixTableLock(RxDeviceObject->pRxNetNameTable);
}
/* If we failed creating, quit */
if (Status != STATUS_SUCCESS)
{
DPRINT1("RxFindOrConstructVirtualNetRoot() = Status: %x\n", Status);
return Status;
}
/* Otherwise, wait until the VNetRoot is stable */
DPRINT("Waiting for stable condition for: %p\n", VNetRoot);
RxWaitForStableVNetRoot(VNetRoot, RxContext);
/* It's all good, update the RX_CONTEXT with all our structs */
if (VNetRoot->Condition == Condition_Good)
{
PNET_ROOT NetRoot;
NetRoot = VNetRoot->NetRoot;
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)NetRoot->SrvCall;
}
else
{
RxDereferenceVNetRoot(VNetRoot, LHS_LockNotHeld);
RxContext->Create.pVNetRoot = NULL;
Status = STATUS_BAD_NETWORK_PATH;
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
RxFindOrCreateConnections(
_In_ PRX_CONTEXT RxContext,
_In_ PUNICODE_STRING CanonicalName,
_In_ NET_ROOT_TYPE NetRootType,
_Out_ PUNICODE_STRING LocalNetRootName,
_Out_ PUNICODE_STRING FilePathName,
_Inout_ PLOCK_HOLDING_STATE LockState,
_In_ PRX_CONNECTION_ID RxConnectionId)
{
PVOID Container;
PSRV_CALL SrvCall;
PNET_ROOT NetRoot;
PV_NET_ROOT VNetRoot;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PRX_PREFIX_TABLE PrefixTable;
UNICODE_STRING RemainingName, NetRootName;
PAGED_CODE();
DPRINT("RxFindOrCreateConnections(%p, %wZ, %x, %p, %p, %p, %p)\n",
RxContext, CanonicalName, NetRootType, LocalNetRootName,
FilePathName, LockState, RxConnectionId);
*FilePathName = *CanonicalName;
LocalNetRootName->Length = 0;
LocalNetRootName->MaximumLength = 0;
LocalNetRootName->Buffer = CanonicalName->Buffer;
/* UNC path, split it */
if (FilePathName->Buffer[1] == ';')
{
BOOLEAN Slash;
USHORT i, Length;
Slash = FALSE;
for (i = 2; i < FilePathName->Length / sizeof(WCHAR); ++i)
{
if (FilePathName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR)
{
Slash = TRUE;
break;
}
}
if (!Slash)
{
return STATUS_OBJECT_NAME_INVALID;
}
FilePathName->Buffer = &FilePathName->Buffer[i];
Length = (USHORT)((ULONG_PTR)FilePathName->Buffer - (ULONG_PTR)LocalNetRootName->Buffer);
LocalNetRootName->Length = Length;
LocalNetRootName->MaximumLength = Length;
FilePathName->Length -= Length;
DPRINT("CanonicalName: %wZ\n", CanonicalName);
DPRINT(" -> FilePathName: %wZ\n", FilePathName);
DPRINT(" -> LocalNetRootName: %wZ\n", LocalNetRootName);
}
Container = NULL;
PrefixTable = RxContext->RxDeviceObject->pRxNetNameTable;
_SEH2_TRY
{
RetryLookup:
ASSERT(*LockState != LHS_LockNotHeld);
/* If previous lookup left something, dereference it */
if (Container != NULL)
{
switch (NodeType(Container))
{
case RDBSS_NTC_SRVCALL:
RxDereferenceSrvCall(Container, *LockState);
break;
case RDBSS_NTC_NETROOT:
RxDereferenceNetRoot(Container, *LockState);
break;
case RDBSS_NTC_V_NETROOT:
RxDereferenceVNetRoot(Container, *LockState);
break;
default:
/* Should never happen */
ASSERT(FALSE);
break;
}
}
/* Look for our NetRoot in prefix table */
Container = RxPrefixTableLookupName(PrefixTable, FilePathName, &RemainingName, RxConnectionId);
DPRINT("Container %p for path %wZ\n", Container, FilePathName);
while (TRUE)
{
UNICODE_STRING SrvCallName;
SrvCall = NULL;
NetRoot = NULL;
VNetRoot = NULL;
/* Assume we didn't succeed */
RxContext->Create.pVNetRoot = NULL;
RxContext->Create.pNetRoot = NULL;
RxContext->Create.pSrvCall = NULL;
RxContext->Create.Type = NetRootType;
/* If we found something */
if (Container != NULL)
{
/* A VNetRoot */
if (NodeType(Container) == RDBSS_NTC_V_NETROOT)
{
VNetRoot = Container;
/* Use its NetRoot */
NetRoot = VNetRoot->NetRoot;
/* If it's not stable, wait for it to be stable */
if (NetRoot->Condition == Condition_InTransition)
{
RxReleasePrefixTableLock(PrefixTable);
DPRINT("Waiting for stable condition for: %p\n", NetRoot);
RxWaitForStableNetRoot(NetRoot, RxContext);
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
*LockState = LHS_ExclusiveLockHeld;
/* Now that's it's ok, retry lookup to find what we want */
if (NetRoot->Condition == Condition_Good)
{
goto RetryLookup;
}
}
/* Is the associated netroot good? */
if (NetRoot->Condition == Condition_Good)
{
SrvCall = (PSRV_CALL)NetRoot->pSrvCall;
/* If it is, and SrvCall as well, then, we have our active connection */
if (SrvCall->Condition == Condition_Good &&
SrvCall->RxDeviceObject == RxContext->RxDeviceObject)
{
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
Status = STATUS_CONNECTION_ACTIVE;
_SEH2_LEAVE;
}
}
/* If VNetRoot was well constructed, it means the connection is active */
if (VNetRoot->ConstructionStatus == STATUS_SUCCESS)
{
Status = STATUS_CONNECTION_ACTIVE;
}
else
{
Status = VNetRoot->ConstructionStatus;
}
RxDereferenceVNetRoot(VNetRoot, *LockState);
_SEH2_LEAVE;
}
/* Can only be a SrvCall */
else
{
ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
SrvCall = Container;
/* Wait for the SRV_CALL to be stable */
if (SrvCall->Condition == Condition_InTransition)
{
RxReleasePrefixTableLock(PrefixTable);
DPRINT("Waiting for stable condition for: %p\n", SrvCall);
RxWaitForStableSrvCall(SrvCall, RxContext);
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
*LockState = LHS_ExclusiveLockHeld;
/* It went good, loop again to find what we look for */
if (SrvCall->Condition == Condition_Good)
{
goto RetryLookup;
}
}
/* If it's not good... */
if (SrvCall->Condition != Condition_Good)
{
/* But SRV_CALL was well constructed, assume a connection was active */
if (SrvCall->Status == STATUS_SUCCESS)
{
Status = STATUS_CONNECTION_ACTIVE;
}
else
{
Status = SrvCall->Status;
}
RxDereferenceSrvCall(SrvCall, *LockState);
_SEH2_LEAVE;
}
}
}
/* If we found a SRV_CALL not matching our DO, quit */
if (SrvCall != NULL && SrvCall->Condition == Condition_Good &&
SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
{
RxDereferenceSrvCall(SrvCall, *LockState);
Status = STATUS_BAD_NETWORK_NAME;
_SEH2_LEAVE;
}
/* Now, we want exclusive lock */
if (*LockState == LHS_SharedLockHeld)
{
if (!RxAcquirePrefixTableLockExclusive(PrefixTable, FALSE))
{
RxReleasePrefixTableLock(PrefixTable);
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
*LockState = LHS_ExclusiveLockHeld;
goto RetryLookup;
}
RxReleasePrefixTableLock(PrefixTable);
*LockState = LHS_ExclusiveLockHeld;
}
ASSERT(*LockState == LHS_ExclusiveLockHeld);
/* If we reach that point, we found something, no need to create something */
if (Container != NULL)
{
break;
}
/* Get the name for the SRV_CALL */
RxExtractServerName(FilePathName, &SrvCallName, NULL);
DPRINT(" -> SrvCallName: %wZ\n", &SrvCallName);
/* And create the SRV_CALL */
SrvCall = RxCreateSrvCall(RxContext, &SrvCallName, NULL, RxConnectionId);
if (SrvCall == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
/* Reset RX_CONTEXT, so far, connection creation isn't a success */
RxReferenceSrvCall(SrvCall);
RxContext->Create.pVNetRoot = NULL;
RxContext->Create.pNetRoot = NULL;
RxContext->Create.pSrvCall = NULL;
RxContext->Create.Type = NetRootType;
Container = SrvCall;
/* Construct SRV_CALL, ie, use mini-rdr */
Status = RxConstructSrvCall(RxContext, SrvCall, LockState);
ASSERT(Status != STATUS_SUCCESS || RxIsPrefixTableLockAcquired(PrefixTable));
if (Status != STATUS_SUCCESS)
{
DPRINT1("RxConstructSrvCall() = Status: %x\n", Status);
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
RxDereferenceSrvCall(SrvCall, *LockState);
RxReleasePrefixTableLock(PrefixTable);
_SEH2_LEAVE;
}
/* Loop again to make use of SRV_CALL stable condition wait */
}
/* At that point, we have a stable SRV_CALL (either found or constructed) */
ASSERT((NodeType(SrvCall) == RDBSS_NTC_SRVCALL) && (SrvCall->Condition == Condition_Good));
ASSERT(NetRoot == NULL && VNetRoot == NULL);
ASSERT(SrvCall->RxDeviceObject == RxContext->RxDeviceObject);
/* Call mini-rdr to get NetRoot name */
SrvCall->RxDeviceObject->Dispatch->MRxExtractNetRootName(FilePathName, (PMRX_SRV_CALL)SrvCall, &NetRootName, NULL);
/* And create the NetRoot with that name */
NetRoot = RxCreateNetRoot(SrvCall, &NetRootName, 0, RxConnectionId);
if (NetRoot == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
NetRoot->Type = NetRootType;
RxDereferenceSrvCall(SrvCall, *LockState);
/* Finally, create the associated VNetRoot */
VNetRoot = RxCreateVNetRoot(RxContext, NetRoot, CanonicalName, LocalNetRootName, FilePathName, RxConnectionId);
if (VNetRoot == NULL)
{
RxFinalizeNetRoot(NetRoot, TRUE, TRUE);
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
RxReferenceVNetRoot(VNetRoot);
/* We're get closer! */
NetRoot->Condition = Condition_InTransition;
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
/* Construct the NetRoot, involving the mini-rdr now that we have our three control structs */
Status = RxConstructNetRoot(RxContext, SrvCall, NetRoot, VNetRoot, LockState);
if (!NT_SUCCESS(Status))
{
RxTransitionVNetRoot(VNetRoot, Condition_Bad);
DPRINT1("RxConstructNetRoot failed Ctxt: %p, VNet: %p, Status: %lx, Condition: %d\n", RxContext, VNetRoot, Status, VNetRoot->Condition);
RxDereferenceVNetRoot(VNetRoot, *LockState);
RxContext->Create.pNetRoot = NULL;
RxContext->Create.pVNetRoot = NULL;
}
else
{
PIO_STACK_LOCATION Stack;
ASSERT(*LockState == LHS_ExclusiveLockHeld);
Stack = RxContext->CurrentIrpSp;
if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_CREATE_TREE_CONNECTION))
{
RxExclusivePrefixTableLockToShared(PrefixTable);
*LockState = LHS_SharedLockHeld;
}
}
}
_SEH2_FINALLY
{
if (Status != STATUS_SUCCESS && Status != STATUS_CONNECTION_ACTIVE)
{
if (*LockState != LHS_LockNotHeld)
{
RxReleasePrefixTableLock(PrefixTable);
*LockState = LHS_LockNotHeld;
}
}
}
_SEH2_END;
DPRINT("RxFindOrCreateConnections() = Status: %x\n", Status);
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
RxFinishFcbInitialization(
IN OUT PMRX_FCB Fcb,
IN RX_FILE_TYPE FileType,
IN PFCB_INIT_PACKET InitPacket OPTIONAL)
{
NODE_TYPE_CODE OldType;
PAGED_CODE();
DPRINT("RxFinishFcbInitialization(%p, %x, %p)\n", Fcb, FileType, InitPacket);
OldType = Fcb->Header.NodeTypeCode;
Fcb->Header.NodeTypeCode = FileType;
/* If mini-rdr already did the job for mailslot attributes, 0 the rest */
if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET) && FileType == RDBSS_NTC_MAILSLOT)
{
FILL_IN_FCB((PFCB)Fcb, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
/* Otherwise, if mini-rdr provided us with an init packet, copy its data */
else if (InitPacket != NULL)
{
FILL_IN_FCB((PFCB)Fcb, *InitPacket->pAttributes, *InitPacket->pNumLinks,
InitPacket->pCreationTime->QuadPart, InitPacket->pLastAccessTime->QuadPart,
InitPacket->pLastWriteTime->QuadPart, InitPacket->pLastChangeTime->QuadPart,
InitPacket->pAllocationSize->QuadPart, InitPacket->pFileSize->QuadPart,
InitPacket->pValidDataLength->QuadPart);
}
if (FileType != RDBSS_NTC_STORAGE_TYPE_UNKNOWN &&
FileType != RDBSS_NTC_STORAGE_TYPE_DIRECTORY)
{
/* If our FCB newly points to a file, initiliaz everything related */
if (FileType == RDBSS_NTC_STORAGE_TYPE_FILE &&
!OldType != RDBSS_NTC_STORAGE_TYPE_FILE)
{
RxInitializeLowIoPerFcbInfo(&((PFCB)Fcb)->Specific.Fcb.LowIoPerFcbInfo);
FsRtlInitializeFileLock(&((PFCB)Fcb)->Specific.Fcb.FileLock, &RxLockOperationCompletion,
&RxUnlockOperation);
((PFCB)Fcb)->BufferedLocks.List = NULL;
((PFCB)Fcb)->BufferedLocks.PendingLockOps = 0;
Fcb->Header.IsFastIoPossible = FastIoIsQuestionable;
}
else
{
ASSERT(FileType >= RDBSS_NTC_SPOOLFILE && FileType <= RDBSS_NTC_MAILSLOT);
}
}
}
/*
* @implemented
*/
NTSTATUS
RxFinishSrvCallConstruction(
PMRX_SRVCALLDOWN_STRUCTURE Calldown)
{
NTSTATUS Status;
PSRV_CALL SrvCall;
PRX_CONTEXT Context;
RX_BLOCK_CONDITION Condition;
PRX_PREFIX_TABLE PrefixTable;
DPRINT("RxFinishSrvCallConstruction(%p)\n", Calldown);
SrvCall = (PSRV_CALL)Calldown->SrvCall;
Context = Calldown->RxContext;
PrefixTable = Context->RxDeviceObject->pRxNetNameTable;
/* We have a winner, notify him */
if (Calldown->BestFinisher != NULL)
{
DPRINT("Notify the winner: %p (%wZ)\n", Calldown->BestFinisher, &Calldown->BestFinisher->DeviceName);
ASSERT(SrvCall->RxDeviceObject == Calldown->BestFinisher);
MINIRDR_CALL_THROUGH(Status, Calldown->BestFinisher->Dispatch,
MRxSrvCallWinnerNotify,
((PMRX_SRV_CALL)SrvCall, TRUE,
Calldown->CallbackContexts[Calldown->BestFinisherOrdinal].RecommunicateContext));
if (Status != STATUS_SUCCESS)
{
Condition = Condition_Bad;
}
else
{
Condition = Condition_Good;
}
}
/* Otherwise, just fail our SRV_CALL */
else
{
Status = Calldown->CallbackContexts[0].Status;
Condition = Condition_Bad;
}
RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
RxTransitionSrvCall(SrvCall, Condition);
ExFreePoolWithTag(Calldown, RX_SRVCALL_POOLTAG);
/* If async, finish it here, otherwise, caller has already finished the stuff */
if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
{
DPRINT("Finishing async call\n");
RxReleasePrefixTableLock(PrefixTable);
/* Make sure we weren't cancelled in-between */
if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_CANCELLED))
{
Status = STATUS_CANCELLED;
}
/* In case that was a create, context can be reused */
if (Context->MajorFunction == IRP_MJ_CREATE)
{
RxpPrepareCreateContextForReuse(Context);
}
/* If that's a failure, reset everything and return failure */
if (Status != STATUS_SUCCESS)
{
Context->MajorFunction = Context->CurrentIrpSp->MajorFunction;
if (Context->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
if (Context->Info.Buffer != NULL)
{
ExFreePool(Context->Info.Buffer);
Context->Info.Buffer = NULL;
}
}
Context->CurrentIrp->IoStatus.Information = 0;
Context->CurrentIrp->IoStatus.Status = Status;
RxCompleteRequest(Context, Status);
}
/* Otherwise, call resume routine and done! */
else
{
Status = Context->ResumeRoutine(Context);
if (Status != STATUS_PENDING)
{
RxCompleteRequest(Context, Status);
}
DPRINT("Not completing, pending\n");
}
}
RxDereferenceSrvCall(SrvCall, LHS_LockNotHeld);
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
RxFinishSrvCallConstructionDispatcher(
IN PVOID Context)
{
KIRQL OldIrql;
BOOLEAN Direct, KeepLoop;
DPRINT("RxFinishSrvCallConstructionDispatcher(%p)\n", Context);
/* In case of failure of starting dispatcher, context is not set
* We keep track of it to fail associated SRV_CALL
*/
Direct = (Context == NULL);
/* Separated thread, loop forever */
while (TRUE)
{
PLIST_ENTRY ListEntry;
PMRX_SRVCALLDOWN_STRUCTURE Calldown;
/* If there are no SRV_CALL to finalize left, just finish thread */
KeAcquireSpinLock(&RxStrucSupSpinLock, &OldIrql);
if (IsListEmpty(&RxSrvCalldownList))
{
KeepLoop = FALSE;
RxSrvCallConstructionDispatcherActive = FALSE;
}
/* Otherwise, get the SRV_CALL to finish construction */
else
{
ListEntry = RemoveHeadList(&RxSrvCalldownList);
KeepLoop = TRUE;
}
KeReleaseSpinLock(&RxStrucSupSpinLock, OldIrql);
/* Nothing to do */
if (!KeepLoop)
{
break;
}
/* If direct is set, reset the finisher to avoid electing a winner
* and fail SRV_CALL (see upper comment)
*/
Calldown = CONTAINING_RECORD(ListEntry, MRX_SRVCALLDOWN_STRUCTURE, SrvCalldownList);
if (Direct)
{
Calldown->BestFinisher = NULL;
}
/* Finish SRV_CALL construction */
RxFinishSrvCallConstruction(Calldown);
}
}
VOID
RxFreeFcbObject(
PVOID Object)
{
UNIMPLEMENTED;
}
VOID
RxFreeObject(
PVOID pObject)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
VOID
RxGetFileSizeWithLock(
IN PFCB Fcb,
OUT PLONGLONG FileSize)
{
PAGED_CODE();
*FileSize = Fcb->Header.FileSize.QuadPart;
}
/*
* @implemented
*/
PEPROCESS
NTAPI
RxGetRDBSSProcess(
VOID)
{
return RxData.OurProcess;
}
/*
* @implemented
*/
NTSTATUS
RxInitializeBufferingManager(
PSRV_CALL SrvCall)
{
KeInitializeSpinLock(&SrvCall->BufferingManager.SpinLock);
InitializeListHead(&SrvCall->BufferingManager.DispatcherList);
InitializeListHead(&SrvCall->BufferingManager.HandlerList);
InitializeListHead(&SrvCall->BufferingManager.LastChanceHandlerList);
SrvCall->BufferingManager.DispatcherActive = FALSE;
SrvCall->BufferingManager.HandlerInactive = FALSE;
SrvCall->BufferingManager.LastChanceHandlerActive = FALSE;
SrvCall->BufferingManager.NumberOfOutstandingOpens = 0;
InitializeListHead(&SrvCall->BufferingManager.SrvOpenLists[0]);
ExInitializeFastMutex(&SrvCall->BufferingManager.Mutex);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID
NTAPI
RxInitializeContext(
IN PIRP Irp,
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN ULONG InitialContextFlags,
IN OUT PRX_CONTEXT RxContext)
{
PIO_STACK_LOCATION Stack;
/* Initialize our various fields */
RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT;
RxContext->NodeByteSize = sizeof(RX_CONTEXT);
RxContext->ReferenceCount = 1;
RxContext->SerialNumber = InterlockedExchangeAdd((volatile LONG *)&RxContextSerialNumberCounter, 1);
RxContext->RxDeviceObject = RxDeviceObject;
KeInitializeEvent(&RxContext->SyncEvent, SynchronizationEvent, FALSE);
RxInitializeScavengerEntry(&RxContext->ScavengerEntry);
InitializeListHead(&RxContext->BlockedOperations);
RxContext->MRxCancelRoutine = NULL;
RxContext->ResumeRoutine = NULL;
RxContext->Flags |= InitialContextFlags;
RxContext->CurrentIrp = Irp;
RxContext->LastExecutionThread = PsGetCurrentThread();
RxContext->OriginalThread = RxContext->LastExecutionThread;
/* If've got no IRP, mark RX_CONTEXT */
if (Irp == NULL)
{
RxContext->CurrentIrpSp = NULL;
RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1;
RxContext->MinorFunction = 0;
}
else
{
/* Otherwise, first determine whether we are performing async operation */
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->FileObject != NULL)
{
PFCB Fcb;
Fcb = Stack->FileObject->FsContext;
if (!IoIsOperationSynchronous(Irp) ||
((Fcb != NULL && NodeTypeIsFcb(Fcb)) &&
(Stack->MajorFunction == IRP_MJ_READ || Stack->MajorFunction == IRP_MJ_WRITE || Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
(Fcb->pNetRoot != NULL && (Fcb->pNetRoot->Type == NET_ROOT_PIPE))))
{
RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
}
}
if (Stack->MajorFunction == IRP_MJ_DIRECTORY_CONTROL && Stack->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
{
RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
}
if (Stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
RxContext->Flags |= RX_CONTEXT_FLAG_ASYNC_OPERATION;
}
/* Set proper flags if TopLevl IRP/Device */
if (!RxIsThisTheTopLevelIrp(Irp))
{
RxContext->Flags |= RX_CONTEXT_FLAG_RECURSIVE_CALL;
}
if (RxGetTopDeviceObjectIfRdbssIrp() == RxDeviceObject)
{
RxContext->Flags |= RX_CONTEXT_FLAG_THIS_DEVICE_TOP_LEVEL;
}
/* Copy stack information */
RxContext->MajorFunction = Stack->MajorFunction;
RxContext->MinorFunction = Stack->MinorFunction;
ASSERT(RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION);
RxContext->CurrentIrpSp = Stack;
/* If we have a FO associated, learn for more */
if (Stack->FileObject != NULL)
{
PFCB Fcb;
PFOBX Fobx;
/* Get the FCB and CCB (FOBX) */
Fcb = Stack->FileObject->FsContext;
Fobx = Stack->FileObject->FsContext2;
RxContext->pFcb = (PMRX_FCB)Fcb;
if (Fcb != NULL && NodeTypeIsFcb(Fcb))
{
RxContext->NonPagedFcb = Fcb->NonPaged;
}
/* We have a FOBX, this not a DFS opening, keep track of it */
if (Fobx != NULL && Fobx != UIntToPtr(DFS_OPEN_CONTEXT) && Fobx != UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT))
{
RxContext->pFobx = (PMRX_FOBX)Fobx;
RxContext->pRelevantSrvOpen = Fobx->pSrvOpen;
if (Fobx->NodeTypeCode == RDBSS_NTC_FOBX)
{
RxContext->FobxSerialNumber = InterlockedIncrement((volatile LONG *)&Fobx->FobxSerialNumber);
}
}
else
{
RxContext->pFobx = NULL;
}
/* In case of directory change notification, Fobx may be a VNetRoot, take note of that */
if (RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL && RxContext->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY &&
Fobx != NULL)
{
PV_NET_ROOT VNetRoot = NULL;
if (Fobx->NodeTypeCode == RDBSS_NTC_FOBX)
{
VNetRoot = Fcb->VNetRoot;
}
else if (Fobx->NodeTypeCode == RDBSS_NTC_V_NETROOT)
{
VNetRoot = (PV_NET_ROOT)Fobx;
}
if (VNetRoot != NULL)
{
RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
}
}
/* Remember if that's a write through file */
RxContext->RealDevice = Stack->FileObject->DeviceObject;
if (BooleanFlagOn(Stack->FileObject->Flags, FO_WRITE_THROUGH))
{
RxContext->Flags |= RX_CONTEXT_FLAG_WRITE_THROUGH;
}
}
}
if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL)
{
DPRINT("New Ctxt: %p for MN: %d, IRP: %p, THRD: %p, FCB: %p, FOBX:%p #%lx\n",
RxContext, RxContext->MinorFunction, Irp,
PsGetCurrentThread(), RxContext->pFcb, RxContext->pFobx,
RxContext->SerialNumber);
}
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RxInitializeDispatcher(
VOID)
{
NTSTATUS Status;
HANDLE ThreadHandle;
PAGED_CODE();
RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
RxFileSystemDeviceObject->DispatcherContext.pTearDownEvent = NULL;
/* Set appropriate timeouts: 10s & 60s */
RxWorkQueueWaitInterval[CriticalWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
RxWorkQueueWaitInterval[DelayedWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
RxWorkQueueWaitInterval[HyperCriticalWorkQueue].QuadPart = -10 * 1000 * 1000 * 10;
RxSpinUpDispatcherWaitInterval.QuadPart = -60 * 1000 * 1000 * 10;
RxDispatcher.NumberOfProcessors = 1;
RxDispatcher.OwnerProcess = IoGetCurrentProcess();
RxDispatcher.pWorkQueueDispatcher = &RxDispatcherWorkQueues;
/* Initialize our dispatchers */
Status = RxInitializeWorkQueueDispatcher(RxDispatcher.pWorkQueueDispatcher);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = RxInitializeMRxDispatcher(RxFileSystemDeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* And start them */
RxDispatcher.State = RxDispatcherActive;
InitializeListHead(&RxDispatcher.SpinUpRequests);
KeInitializeSpinLock(&RxDispatcher.SpinUpRequestsLock);
KeInitializeEvent(&RxDispatcher.SpinUpRequestsEvent, 0, 0);
KeInitializeEvent(&RxDispatcher.SpinUpRequestsTearDownEvent, 0, 0);
Status = PsCreateSystemThread(&ThreadHandle, PROCESS_ALL_ACCESS, NULL,
NULL, NULL, RxSpinUpRequestsDispatcher, &RxDispatcher);
if (NT_SUCCESS(Status))
{
ZwClose(ThreadHandle);
}
return Status;
}
/*
* @implemented
*/
VOID
RxInitializeFcbTable(
IN OUT PRX_FCB_TABLE FcbTable,
IN BOOLEAN CaseInsensitiveMatch)
{
USHORT i;
PAGED_CODE();
FcbTable->NodeTypeCode = RDBSS_NTC_FCB_TABLE;
FcbTable->NodeByteSize = sizeof(RX_FCB_TABLE);
ExInitializeResourceLite(&FcbTable->TableLock);
FcbTable->CaseInsensitiveMatch = CaseInsensitiveMatch;
FcbTable->Version = 0;
FcbTable->TableEntryForNull = NULL;
FcbTable->NumberOfBuckets = RX_FCB_TABLE_NUMBER_OF_HASH_BUCKETS;
for (i = 0; i < FcbTable->NumberOfBuckets; ++i)
{
InitializeListHead(&FcbTable->HashBuckets[i]);
}
FcbTable->Lookups = 0;
FcbTable->FailedLookups = 0;
FcbTable->Compares = 0;
}
/*
* @implemented
*/
VOID
NTAPI
RxInitializeLowIoContext(
OUT PLOWIO_CONTEXT LowIoContext,
IN ULONG Operation)
{
PRX_CONTEXT RxContext;
PIO_STACK_LOCATION Stack;
PAGED_CODE();
RxContext = CONTAINING_RECORD(LowIoContext, RX_CONTEXT, LowIoContext);
ASSERT(LowIoContext = &RxContext->LowIoContext);
Stack = RxContext->CurrentIrpSp;
KeInitializeEvent(&RxContext->SyncEvent, NotificationEvent, FALSE);
RxContext->LowIoContext.ResourceThreadId = (ERESOURCE_THREAD)PsGetCurrentThread();
RxContext->LowIoContext.Operation = Operation;
switch (Operation)
{
case LOWIO_OP_READ:
case LOWIO_OP_WRITE:
/* In case of RW, set a canary, to make sure these fields are properly set
* they will be asserted when lowio request will be submit to mini-rdr
* See LowIoSubmit()
*/
RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset = 0xFFFFFFEE;
RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount = 0xEEEEEEEE;
RxContext->LowIoContext.ParamsFor.ReadWrite.Key = Stack->Parameters.Read.Key;
/* Keep track of paging IOs */
if (BooleanFlagOn(RxContext->CurrentIrp->Flags, IRP_PAGING_IO))
{
RxContext->LowIoContext.ParamsFor.ReadWrite.Flags = LOWIO_READWRITEFLAG_PAGING_IO;
}
else
{
RxContext->LowIoContext.ParamsFor.ReadWrite.Flags = 0;
}
break;
case LOWIO_OP_FSCTL:
case LOWIO_OP_IOCTL:
/* This will be initialized later on with a call to RxLowIoPopulateFsctlInfo() */
RxContext->LowIoContext.ParamsFor.FsCtl.Flags = 0;
RxContext->LowIoContext.ParamsFor.FsCtl.InputBufferLength = 0;
RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = NULL;
RxContext->LowIoContext.ParamsFor.FsCtl.OutputBufferLength = 0;
RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = NULL;
RxContext->LowIoContext.ParamsFor.FsCtl.MinorFunction = 0;
break;
/* Nothing to do for these */
case LOWIO_OP_SHAREDLOCK:
case LOWIO_OP_EXCLUSIVELOCK:
case LOWIO_OP_UNLOCK:
case LOWIO_OP_UNLOCK_MULTIPLE:
case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
case LOWIO_OP_CLEAROUT:
break;
default:
/* Should never happen */
ASSERT(FALSE);
break;
}
}
/*
* @implemented
*/
VOID
RxInitializeLowIoPerFcbInfo(
PLOWIO_PER_FCB_INFO LowIoPerFcbInfo)
{
PAGED_CODE();
InitializeListHead(&LowIoPerFcbInfo->PagingIoReadsOutstanding);
InitializeListHead(&LowIoPerFcbInfo->PagingIoWritesOutstanding);
}
/*
* @implemented
*/
NTSTATUS
RxInitializeMRxDispatcher(
IN OUT PRDBSS_DEVICE_OBJECT pMRxDeviceObject)
{
PAGED_CODE();
pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
pMRxDeviceObject->DispatcherContext.pTearDownEvent = NULL;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID
RxInitializePrefixTable(
IN OUT PRX_PREFIX_TABLE ThisTable,
IN ULONG TableSize OPTIONAL,
IN BOOLEAN CaseInsensitiveMatch)
{
PAGED_CODE();
if (TableSize == 0)
{
TableSize = RX_PREFIX_TABLE_DEFAULT_LENGTH;
}
ThisTable->NodeTypeCode = RDBSS_NTC_PREFIX_TABLE;
ThisTable->NodeByteSize = sizeof(RX_PREFIX_TABLE);
InitializeListHead(&ThisTable->MemberQueue);
ThisTable->Version = 0;
ThisTable->TableEntryForNull = NULL;
ThisTable->IsNetNameTable = FALSE;
ThisTable->CaseInsensitiveMatch = CaseInsensitiveMatch;
ThisTable->TableSize = TableSize;
if (TableSize > 0)
{
USHORT i;
for (i = 0; i < RX_PREFIX_TABLE_DEFAULT_LENGTH; ++i)
{
InitializeListHead(&ThisTable->HashBuckets[i]);
}
}
}
/*
* @implemented
*/
VOID
RxInitializePurgeSyncronizationContext(
PPURGE_SYNCHRONIZATION_CONTEXT PurgeSyncronizationContext)
{
PAGED_CODE();
InitializeListHead(&PurgeSyncronizationContext->ContextsAwaitingPurgeCompletion);
PurgeSyncronizationContext->PurgeInProgress = FALSE;
}
NTSTATUS
RxInitializeSrvCallParameters(
IN PRX_CONTEXT RxContext,
IN OUT PSRV_CALL SrvCall)
{
PAGED_CODE();
SrvCall->pPrincipalName = NULL;
/* We only have stuff to initialize for file opening from DFS */
if (RxContext->MajorFunction != IRP_MJ_CREATE || RxContext->Create.EaLength == 0)
{
return STATUS_SUCCESS;
}
ASSERT(RxContext->Create.EaBuffer != NULL);
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
RxInitializeVNetRootParameters(
PRX_CONTEXT RxContext,
OUT LUID *LogonId,
OUT PULONG SessionId,
OUT PUNICODE_STRING *UserNamePtr,
OUT PUNICODE_STRING *UserDomainNamePtr,
OUT PUNICODE_STRING *PasswordPtr,
OUT PULONG Flags)
{
NTSTATUS Status;
PACCESS_TOKEN Token;
PAGED_CODE();
DPRINT("RxInitializeVNetRootParameters(%p, %p, %p, %p, %p, %p, %p)\n", RxContext,
LogonId, SessionId, UserNamePtr, UserDomainNamePtr, PasswordPtr, Flags);
*UserNamePtr = NULL;
*UserDomainNamePtr = NULL;
*PasswordPtr = NULL;
/* By default, that's not CSC instance */
*Flags &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE;
Token = SeQuerySubjectContextToken(&RxContext->Create.NtCreateParameters.SecurityContext->AccessState->SubjectSecurityContext);
if (SeTokenIsRestricted(Token))
{
return STATUS_ACCESS_DENIED;
}
/* Get LogonId */
Status = SeQueryAuthenticationIdToken(Token, LogonId);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* And SessionId */
Status = SeQuerySessionIdToken(Token, SessionId);
if (!NT_SUCCESS(Status))
{
return Status;
}
if (RxContext->Create.UserName.Buffer != NULL)
{
UNIMPLEMENTED;
Status = STATUS_NOT_IMPLEMENTED;
goto Leave;
}
/* Deal with connection credentials */
if (RxContext->Create.UserDomainName.Buffer != NULL)
{
UNIMPLEMENTED;
Status = STATUS_NOT_IMPLEMENTED;
goto Leave;
}
if (RxContext->Create.Password.Buffer != NULL)
{
UNIMPLEMENTED;
Status = STATUS_NOT_IMPLEMENTED;
goto Leave;
}
Leave:
if (NT_SUCCESS(Status))
{
/* If that's a CSC instance, mark it as such */
if (RxIsThisACscAgentOpen(RxContext))
{
*Flags |= VNETROOT_FLAG_CSCAGENT_INSTANCE;
}
return Status;
}
return Status;
}
/*
* @implemented
*/
VOID
RxInitializeWorkQueue(
PRX_WORK_QUEUE WorkQueue,
WORK_QUEUE_TYPE WorkQueueType,
ULONG MaximumNumberOfWorkerThreads,
ULONG MinimumNumberOfWorkerThreads)
{
PAGED_CODE();
WorkQueue->Type = WorkQueueType;
WorkQueue->MaximumNumberOfWorkerThreads = MaximumNumberOfWorkerThreads;
WorkQueue->MinimumNumberOfWorkerThreads = MinimumNumberOfWorkerThreads;
WorkQueue->State = RxWorkQueueActive;
WorkQueue->SpinUpRequestPending = FALSE;
WorkQueue->pRundownContext = NULL;
WorkQueue->NumberOfWorkItemsDispatched = 0;
WorkQueue->NumberOfWorkItemsToBeDispatched = 0;
WorkQueue->CumulativeQueueLength = 0;
WorkQueue->NumberOfSpinUpRequests = 0;
WorkQueue->NumberOfActiveWorkerThreads = 0;
WorkQueue->NumberOfIdleWorkerThreads = 0;
WorkQueue->NumberOfFailedSpinUpRequests = 0;
WorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse = 0;
WorkQueue->WorkQueueItemForTearDownWorkQueue.List.Flink = NULL;
WorkQueue->WorkQueueItemForTearDownWorkQueue.WorkerRoutine = NULL;
WorkQueue->WorkQueueItemForTearDownWorkQueue.Parameter = NULL;
WorkQueue->WorkQueueItemForTearDownWorkQueue.pDeviceObject = NULL;
WorkQueue->WorkQueueItemForSpinUpWorkerThread.List.Flink = NULL;
WorkQueue->WorkQueueItemForSpinUpWorkerThread.WorkerRoutine = NULL;
WorkQueue->WorkQueueItemForSpinUpWorkerThread.Parameter = NULL;
WorkQueue->WorkQueueItemForSpinUpWorkerThread.pDeviceObject = NULL;
WorkQueue->WorkQueueItemForSpinDownWorkerThread.List.Flink = NULL;
WorkQueue->WorkQueueItemForSpinDownWorkerThread.WorkerRoutine = NULL;
WorkQueue->WorkQueueItemForSpinDownWorkerThread.Parameter = NULL;
WorkQueue->WorkQueueItemForSpinDownWorkerThread.pDeviceObject = NULL;
KeInitializeQueue(&WorkQueue->Queue, MaximumNumberOfWorkerThreads);
KeInitializeSpinLock(&WorkQueue->SpinLock);
}
/*
* @implemented
*/
NTSTATUS
RxInitializeWorkQueueDispatcher(
PRX_WORK_QUEUE_DISPATCHER Dispatcher)
{
NTSTATUS Status;
ULONG MaximumNumberOfWorkerThreads;
PAGED_CODE();
/* Number of threads will depend on system capacity */
if (MmQuerySystemSize() != MmLargeSystem)
{
MaximumNumberOfWorkerThreads = 5;
}
else
{
MaximumNumberOfWorkerThreads = 10;
}
/* Initialize the work queues */
RxInitializeWorkQueue(&Dispatcher->WorkQueue[CriticalWorkQueue], CriticalWorkQueue,
MaximumNumberOfWorkerThreads, 1);
RxInitializeWorkQueue(&Dispatcher->WorkQueue[DelayedWorkQueue], DelayedWorkQueue, 2, 1);
RxInitializeWorkQueue(&Dispatcher->WorkQueue[HyperCriticalWorkQueue], HyperCriticalWorkQueue, 5, 1);
/* And start the worker threads */
Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[HyperCriticalWorkQueue],
RxBootstrapWorkerThreadDispatcher,
&Dispatcher->WorkQueue[HyperCriticalWorkQueue]);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[CriticalWorkQueue],
RxBootstrapWorkerThreadDispatcher,
&Dispatcher->WorkQueue[CriticalWorkQueue]);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = RxSpinUpWorkerThread(&Dispatcher->WorkQueue[DelayedWorkQueue],
RxBootstrapWorkerThreadDispatcher,
&Dispatcher->WorkQueue[DelayedWorkQueue]);
return Status;
}
VOID
RxInitiateSrvOpenKeyAssociation (
IN OUT PSRV_OPEN SrvOpen
)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
NTSTATUS
RxInsertWorkQueueItem(
PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
WORK_QUEUE_TYPE WorkQueueType,
PRX_WORK_DISPATCH_ITEM DispatchItem)
{
KIRQL OldIrql;
NTSTATUS Status;
BOOLEAN SpinUpThreads;
PRX_WORK_QUEUE WorkQueue;
/* No dispatcher, nothing to insert */
if (RxDispatcher.State != RxDispatcherActive)
{
return STATUS_UNSUCCESSFUL;
}
/* Get the work queue */
WorkQueue = &RxDispatcher.pWorkQueueDispatcher->WorkQueue[WorkQueueType];
KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
/* Only insert if the work queue is in decent state */
if (WorkQueue->State != RxWorkQueueActive || pMRxDeviceObject->DispatcherContext.pTearDownEvent != NULL)
{
Status = STATUS_UNSUCCESSFUL;
}
else
{
SpinUpThreads = FALSE;
DispatchItem->WorkQueueItem.pDeviceObject = pMRxDeviceObject;
InterlockedIncrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
WorkQueue->CumulativeQueueLength += WorkQueue->NumberOfWorkItemsToBeDispatched;
InterlockedIncrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
/* If required (and possible!), spin up a new worker thread */
if (WorkQueue->NumberOfIdleWorkerThreads < WorkQueue->NumberOfWorkItemsToBeDispatched &&
WorkQueue->NumberOfActiveWorkerThreads < WorkQueue->MaximumNumberOfWorkerThreads &&
!WorkQueue->SpinUpRequestPending)
{
WorkQueue->SpinUpRequestPending = TRUE;
SpinUpThreads = TRUE;
}
Status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
/* If we failed, return and still not insert item */
if (!NT_SUCCESS(Status))
{
return Status;
}
/* All fine, insert the item */
KeInsertQueue(&WorkQueue->Queue, &DispatchItem->WorkQueueItem.List);
/* And start a new worker thread if needed */
if (SpinUpThreads)
{
RxSpinUpWorkerThreads(WorkQueue);
}
return Status;
}
BOOLEAN
RxIsThisACscAgentOpen(
IN PRX_CONTEXT RxContext)
{
BOOLEAN CscAgent;
CscAgent = FALSE;
/* Client Side Caching is DFS stuff - we don't support it */
if (RxContext->Create.EaLength != 0)
{
UNIMPLEMENTED;
}
if (RxContext->Create.NtCreateParameters.DfsNameContext != NULL &&
((PDFS_NAME_CONTEXT)RxContext->Create.NtCreateParameters.DfsNameContext)->NameContextType == 0xAAAAAAAA)
{
CscAgent = TRUE;
}
return CscAgent;
}
VOID
RxLockUserBuffer(
IN PRX_CONTEXT RxContext,
IN LOCK_OPERATION Operation,
IN ULONG BufferLength)
{
PIRP Irp;
PMDL Mdl = NULL;
PAGED_CODE();
_SEH2_TRY
{
Irp = RxContext->CurrentIrp;
/* If we already have a MDL, make sure it's locked */
if (Irp->MdlAddress != NULL)
{
ASSERT(RxLowIoIsMdlLocked(Irp->MdlAddress));
}
else
{
/* That likely means the driver asks for buffered IOs - we don't support it! */
ASSERT(!BooleanFlagOn(Irp->Flags, IRP_INPUT_OPERATION));
/* If we have a real length */
if (BufferLength > 0)
{
/* Allocate a MDL and lock it */
Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
if (Mdl == NULL)
{
RxContext->StoredStatus = STATUS_INSUFFICIENT_RESOURCES;
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
MmProbeAndLockPages(Mdl, Irp->RequestorMode, Operation);
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
NTSTATUS Status;
Status = _SEH2_GetExceptionCode();
/* Free the possible MDL we have allocated */
IoFreeMdl(Mdl);
Irp->MdlAddress = NULL;
RxContext->Flags |= RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT;
/* Fix status */
if (!FsRtlIsNtstatusExpected(Status))
{
Status = STATUS_INVALID_USER_BUFFER;
}
RxContext->IoStatusBlock.Status = Status;
ExRaiseStatus(Status);
}
_SEH2_END;
}
NTSTATUS
RxLowIoCompletionTail(
IN PRX_CONTEXT RxContext)
{
NTSTATUS Status;
USHORT Operation;
PAGED_CODE();
DPRINT("RxLowIoCompletionTail(%p)\n", RxContext);
/* Only continue if we're at APC_LEVEL or lower */
if (KeGetCurrentIrql() >= DISPATCH_LEVEL &&
!BooleanFlagOn(RxContext->LowIoContext.Flags, LOWIO_CONTEXT_FLAG_CAN_COMPLETE_AT_DPC_LEVEL))
{
return STATUS_MORE_PROCESSING_REQUIRED;
}
/* Call the completion routine */
DPRINT("Calling completion routine: %p\n", RxContext->LowIoContext.CompletionRoutine);
Status = RxContext->LowIoContext.CompletionRoutine(RxContext);
if (Status == STATUS_MORE_PROCESSING_REQUIRED || Status == STATUS_RETRY)
{
return Status;
}
/* If it was a RW operation, for a paging file ... */
Operation = RxContext->LowIoContext.Operation;
if (Operation == LOWIO_OP_READ || Operation == LOWIO_OP_WRITE)
{
if (BooleanFlagOn(RxContext->LowIoContext.ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO))
{
UNIMPLEMENTED;
Status = STATUS_NOT_IMPLEMENTED;
}
}
else
{
/* Sanity check: we had known operation */
ASSERT(Operation < LOWIO_OP_MAXIMUM);
}
/* If not sync operation, complete now. Otherwise, caller has already completed */
if (!BooleanFlagOn(RxContext->LowIoContext.Flags, LOWIO_CONTEXT_FLAG_SYNCCALL))
{
RxCompleteRequest(RxContext, Status);
}
DPRINT("Status: %x\n", Status);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RxLowIoPopulateFsctlInfo(
IN PRX_CONTEXT RxContext)
{
PMDL Mdl;
PIRP Irp;
UCHAR Method;
PIO_STACK_LOCATION Stack;
PAGED_CODE();
DPRINT("RxLowIoPopulateFsctlInfo(%p)\n", RxContext);
Irp = RxContext->CurrentIrp;
Stack = RxContext->CurrentIrpSp;
/* Copy stack parameters */
RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode = Stack->Parameters.FileSystemControl.FsControlCode;
RxContext->LowIoContext.ParamsFor.FsCtl.InputBufferLength = Stack->Parameters.FileSystemControl.InputBufferLength;
RxContext->LowIoContext.ParamsFor.FsCtl.OutputBufferLength = Stack->Parameters.FileSystemControl.OutputBufferLength;
RxContext->LowIoContext.ParamsFor.FsCtl.MinorFunction = Stack->MinorFunction;
Method = METHOD_FROM_CTL_CODE(RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode);
/* Same buffer in case of buffered */
if (Method == METHOD_BUFFERED)
{
RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = Irp->AssociatedIrp.SystemBuffer;
return STATUS_SUCCESS;
}
/* Two buffers for neither */
if (Method == METHOD_NEITHER)
{
RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Stack->Parameters.FileSystemControl.Type3InputBuffer;
RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = Irp->UserBuffer;
return STATUS_SUCCESS;
}
/* Only IN/OUT remain */
ASSERT(Method == METHOD_IN_DIRECT || Method == METHOD_OUT_DIRECT);
/* Use system buffer for input */
RxContext->LowIoContext.ParamsFor.FsCtl.pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
/* And MDL for output */
Mdl = Irp->MdlAddress;
if (Mdl != NULL)
{
RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
if (RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
RxContext->LowIoContext.ParamsFor.FsCtl.pOutputBuffer = NULL;
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RxLowIoSubmit(
IN PRX_CONTEXT RxContext,
IN PLOWIO_COMPLETION_ROUTINE CompletionRoutine)
{
NTSTATUS Status;
USHORT Operation;
BOOLEAN Synchronous;
PLOWIO_CONTEXT LowIoContext;
DPRINT("RxLowIoSubmit(%p, %p)\n", RxContext, CompletionRoutine);
PAGED_CODE();
LowIoContext = &RxContext->LowIoContext;
Synchronous = !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION);
LowIoContext->CompletionRoutine = CompletionRoutine;
Status = STATUS_SUCCESS;
Operation = LowIoContext->Operation;
switch (Operation)
{
case LOWIO_OP_READ:
case LOWIO_OP_WRITE:
/* Check that the parameters were properly set by caller
* See comment in RxInitializeLowIoContext()
*/
ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteOffset != 0xFFFFFFEE);
ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteCount != 0xEEEEEEEE);
/* Lock the buffer */
RxLockUserBuffer(RxContext,
(Operation == LOWIO_OP_READ ? IoWriteAccess : IoReadAccess),
LowIoContext->ParamsFor.ReadWrite.ByteCount);
if (RxNewMapUserBuffer(RxContext) == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
LowIoContext->ParamsFor.ReadWrite.Buffer = RxContext->CurrentIrp->MdlAddress;
/* If that's a paging IO, initialize serial operation */
if (BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO))
{
PFCB Fcb;
Fcb = (PFCB)RxContext->pFcb;
ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
RxContext->BlockedOpsMutex = &RxLowIoPagingIoSyncMutex;
if (Operation == LOWIO_OP_READ)
{
InsertTailList(&Fcb->Specific.Fcb.PagingIoReadsOutstanding, &RxContext->RxContextSerializationQLinks);
}
else
{
InsertTailList(&Fcb->Specific.Fcb.PagingIoWritesOutstanding, &RxContext->RxContextSerializationQLinks);
}
ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
}
break;
case LOWIO_OP_FSCTL:
case LOWIO_OP_IOCTL:
/* Set FSCTL/IOCTL parameters */
Status = RxLowIoPopulateFsctlInfo(RxContext);
/* Check whether we're consistent: a length means a buffer */
if (NT_SUCCESS(Status))
{
if ((LowIoContext->ParamsFor.FsCtl.InputBufferLength > 0 &&
LowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL) ||
(LowIoContext->ParamsFor.FsCtl.OutputBufferLength > 0 &&
LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL))
{
Status = STATUS_INVALID_PARAMETER;
}
}
break;
/* Nothing to do */
case LOWIO_OP_SHAREDLOCK:
case LOWIO_OP_EXCLUSIVELOCK:
case LOWIO_OP_UNLOCK:
case LOWIO_OP_UNLOCK_MULTIPLE:
case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
case LOWIO_OP_CLEAROUT:
break;
default:
ASSERT(FALSE);
Status = STATUS_INVALID_PARAMETER;
break;
}
/* No need to perform extra init in case of posting */
RxContext->Flags |= RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED;
/* Preflight checks were OK, time to submit */
if (NT_SUCCESS(Status))
{
PMINIRDR_DISPATCH Dispatch;
if (!Synchronous)
{
InterlockedIncrement((volatile long *)&RxContext->ReferenceCount);
/* If not synchronous, we're likely to return before the operation is finished */
if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP))
{
IoMarkIrpPending(RxContext->CurrentIrp);
}
}
Dispatch = RxContext->RxDeviceObject->Dispatch;
if (Dispatch != NULL)
{
/* We'll try to execute until the mini-rdr doesn't return pending */
do
{
RxContext->IoStatusBlock.Information = 0;
MINIRDR_CALL(Status, RxContext, Dispatch, MRxLowIOSubmit[Operation], (RxContext));
if (Status == STATUS_PENDING)
{
/* Unless it's not synchronous, caller will be happy with pending op */
if (!Synchronous)
{
return Status;
}
RxWaitSync(RxContext);
Status = RxContext->IoStatusBlock.Status;
}
else
{
if (!Synchronous)
{
/* We had marked the IRP pending, whereas the operation finished, drop that */
if (Status != STATUS_RETRY)
{
if (!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP))
{
RxContext->CurrentIrpSp->Flags &= ~SL_PENDING_RETURNED;
}
InterlockedDecrement((volatile long *)&RxContext->ReferenceCount);
}
}
}
} while (Status == STATUS_PENDING);
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
}
/* Call completion and return */
RxContext->IoStatusBlock.Status = Status;
LowIoContext->Flags |= LOWIO_CONTEXT_FLAG_SYNCCALL;
return RxLowIoCompletionTail(RxContext);
}
/*
* @implemented
*/
PVOID
RxMapSystemBuffer(
IN PRX_CONTEXT RxContext)
{
PIRP Irp;
PAGED_CODE();
Irp = RxContext->CurrentIrp;
/* We should have a MDL (buffered IOs are not supported!) */
if (Irp->MdlAddress != NULL)
{
ASSERT(FALSE);
return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
/* Just return system buffer */
return Irp->AssociatedIrp.SystemBuffer;
}
VOID
RxMarkFobxOnCleanup(
PFOBX pFobx,
PBOOLEAN NeedPurge)
{
UNIMPLEMENTED;
}
VOID
RxMarkFobxOnClose(
PFOBX Fobx)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
PVOID
RxNewMapUserBuffer(
PRX_CONTEXT RxContext)
{
PIRP Irp;
PAGED_CODE();
Irp = RxContext->CurrentIrp;
if (Irp->MdlAddress != NULL)
{
return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
return Irp->UserBuffer;
}
BOOLEAN
NTAPI
RxNoOpAcquire(
IN PVOID Fcb,
IN BOOLEAN Wait)
{
UNIMPLEMENTED;
return FALSE;
}
VOID
NTAPI
RxNoOpRelease(
IN PVOID Fcb)
{
UNIMPLEMENTED;
}
VOID
RxOrphanThisFcb(
PFCB Fcb)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
BOOLEAN
RxpAcquirePrefixTableLockShared(
PRX_PREFIX_TABLE pTable,
BOOLEAN Wait,
BOOLEAN ProcessBufferingStateChangeRequests)
{
PAGED_CODE();
DPRINT("RxpAcquirePrefixTableLockShared(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
pTable->TableLock.ActiveEntries);
return ExAcquireResourceSharedLite(&pTable->TableLock, Wait);
}
/*
* @implemented
*/
BOOLEAN
RxpAcquirePrefixTableLockExclusive(
PRX_PREFIX_TABLE pTable,
BOOLEAN Wait,
BOOLEAN ProcessBufferingStateChangeRequests)
{
PAGED_CODE();
DPRINT("RxpAcquirePrefixTableLockExclusive(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
pTable->TableLock.ActiveEntries);
return ExAcquireResourceExclusiveLite(&pTable->TableLock, Wait);
}
/*
* @implemented
*/
BOOLEAN
RxpDereferenceAndFinalizeNetFcb(
OUT PFCB ThisFcb,
IN PRX_CONTEXT RxContext,
IN BOOLEAN RecursiveFinalize,
IN BOOLEAN ForceFinalize)
{
NTSTATUS Status;
ULONG References;
PNET_ROOT NetRoot;
BOOLEAN ResourceAcquired, NetRootReferenced, Freed;
PAGED_CODE();
ASSERT(!ForceFinalize);
ASSERT(NodeTypeIsFcb(ThisFcb));
ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
/* Unless we're recursively finalizing, or forcing, if FCB is still in use, quit */
References = InterlockedDecrement((volatile long *)&ThisFcb->NodeReferenceCount);
if (!ForceFinalize && !RecursiveFinalize && (ThisFcb->OpenCount != 0 || ThisFcb->UncleanCount != 0 || References > 1))
{
return FALSE;
}
Freed = FALSE;
Status = STATUS_SUCCESS;
NetRoot = (PNET_ROOT)ThisFcb->VNetRoot->pNetRoot;
ResourceAcquired = FALSE;
NetRootReferenced = FALSE;
/* If FCB isn't orphaned, it still have context attached */
if (!BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_ORPHANED))
{
/* Don't let NetRoot go away before we're done */
RxReferenceNetRoot(NetRoot);
NetRootReferenced = TRUE;
/* Try to acquire the table lock exclusively */
if (!RxIsFcbTableLockExclusive(&NetRoot->FcbTable))
{
RxReferenceNetFcb(ThisFcb);
if (!RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, FALSE))
{
if (RxContext != NULL && RxContext != (PVOID)-1 && RxContext != (PVOID)-2)
{
RxContext->Flags |= RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK;
}
RxReleaseFcb(RxContext, ThisFcb);
RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
Status = RxAcquireExclusiveFcb(RxContext, ThisFcb);
}
References = RxDereferenceNetFcb(ThisFcb);
ResourceAcquired = TRUE;
}
}
/* If locking was OK (or not needed!), attempt finalization */
if (NT_SUCCESS(Status))
{
Freed = RxFinalizeNetFcb(ThisFcb, RecursiveFinalize, ForceFinalize, References);
}
/* Release table lock if acquired */
if (ResourceAcquired)
{
RxReleaseFcbTableLock(&NetRoot->FcbTable);
}
/* We don't need the NetRoot anylonger */
if (NetRootReferenced)
{
RxDereferenceNetRoot(NetRoot, LHS_LockNotHeld);
}
return Freed;
}
LONG
RxpDereferenceNetFcb(
PFCB Fcb)
{
UNIMPLEMENTED;
return 0;
}
/*
* @implemented
*/
PRX_PREFIX_ENTRY
RxPrefixTableInsertName(
IN OUT PRX_PREFIX_TABLE ThisTable,
IN OUT PRX_PREFIX_ENTRY ThisEntry,
IN PVOID Container,
IN PULONG ContainerRefCount,
IN USHORT CaseInsensitiveLength,
IN PRX_CONNECTION_ID ConnectionId
)
{
PAGED_CODE();
DPRINT("Insert: %wZ\n", &ThisEntry->Prefix);
ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
ASSERT(CaseInsensitiveLength <= ThisEntry->Prefix.Length);
/* Copy parameters and compute hash */
ThisEntry->CaseInsensitiveLength = CaseInsensitiveLength;
ThisEntry->ContainingRecord = Container;
ThisEntry->ContainerRefCount = ContainerRefCount;
InterlockedIncrement((volatile long *)ContainerRefCount);
ThisEntry->SavedHashValue = RxTableComputeHashValue(&ThisEntry->Prefix);
DPRINT("Associated hash: %x\n", ThisEntry->SavedHashValue);
/* If no path length: this is entry for null path */
if (ThisEntry->Prefix.Length == 0)
{
ThisTable->TableEntryForNull = ThisEntry;
}
/* Otherwise, insert in the appropriate bucket */
else
{
InsertTailList(HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue), &ThisEntry->HashLinks);
}
/* If we had a connection ID, keep track of it */
if (ConnectionId != NULL)
{
ThisEntry->ConnectionId.Luid = ConnectionId->Luid;
}
else
{
ThisEntry->ConnectionId.Luid.LowPart = 0;
ThisEntry->ConnectionId.Luid.HighPart = 0;
}
InsertTailList(&ThisTable->MemberQueue, &ThisEntry->MemberQLinks);
/* Reflect the changes */
++ThisTable->Version;
DPRINT("Inserted in bucket: %p\n", HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue));
return ThisEntry;
}
/*
* @implemented
*/
PVOID
RxPrefixTableLookupName(
IN PRX_PREFIX_TABLE ThisTable,
IN PUNICODE_STRING CanonicalName,
OUT PUNICODE_STRING RemainingName,
IN PRX_CONNECTION_ID ConnectionId)
{
PVOID Container;
PAGED_CODE();
ASSERT(RxIsPrefixTableLockAcquired(ThisTable));
ASSERT(CanonicalName->Length > 0);
/* Call the internal helper */
Container = RxTableLookupName(ThisTable, CanonicalName, RemainingName, ConnectionId);
if (Container == NULL)
{
return NULL;
}
/* Reference our container before returning it */
if (RdbssReferenceTracingValue != 0)
{
NODE_TYPE_CODE Type;
Type = NodeType(Container);
switch (Type)
{
case RDBSS_NTC_SRVCALL:
RxReferenceSrvCall(Container);
break;
case RDBSS_NTC_NETROOT:
RxReferenceNetRoot(Container);
break;
case RDBSS_NTC_V_NETROOT:
RxReferenceVNetRoot(Container);
break;
default:
ASSERT(FALSE);
break;
}
}
else
{
RxReference(Container);
}
return Container;
}
LONG
RxpReferenceNetFcb(
PFCB Fcb)
{
UNIMPLEMENTED;
return 0;
}
/*
* @implemented
*/
VOID
RxpReleasePrefixTableLock(
PRX_PREFIX_TABLE pTable,
BOOLEAN ProcessBufferingStateChangeRequests)
{
PAGED_CODE();
DPRINT("RxpReleasePrefixTableLock(%p, %d) -> %d\n", pTable, ProcessBufferingStateChangeRequests,
pTable->TableLock.ActiveEntries);
ExReleaseResourceLite(&pTable->TableLock);
}
/*
* @implemented
*/
VOID
NTAPI
RxPrepareContextForReuse(
IN OUT PRX_CONTEXT RxContext)
{
PAGED_CODE();
/* When we reach that point, make sure mandatory parts are null-ed */
if (RxContext->MajorFunction == IRP_MJ_CREATE)
{
ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
RxContext->Create.RdrFlags = 0;
}
else if (RxContext->MajorFunction == IRP_MJ_READ || RxContext->MajorFunction == IRP_MJ_WRITE)
{
ASSERT(RxContext->RxContextSerializationQLinks.Flink == NULL);
ASSERT(RxContext->RxContextSerializationQLinks.Blink == NULL);
}
RxContext->ReferenceCount = 0;
}
VOID
RxProcessFcbChangeBufferingStateRequest(
PFCB Fcb)
{
UNIMPLEMENTED;
}
BOOLEAN
RxpTrackDereference(
_In_ ULONG TraceType,
_In_ PCSTR FileName,
_In_ ULONG Line,
_In_ PVOID Instance)
{
UNIMPLEMENTED;
return FALSE;
}
VOID
RxpTrackReference(
_In_ ULONG TraceType,
_In_ PCSTR FileName,
_In_ ULONG Line,
_In_ PVOID Instance)
{
UNIMPLEMENTED;
}
VOID
RxpUndoScavengerFinalizationMarking(
PVOID Instance)
{
PNODE_TYPE_AND_SIZE Node;
PAGED_CODE();
Node = (PNODE_TYPE_AND_SIZE)Instance;
/* There's no marking - nothing to do */
if (!BooleanFlagOn(Node->NodeTypeCode, RX_SCAVENGER_MASK))
{
return;
}
UNIMPLEMENTED;
}
NTSTATUS
RxPurgeFcbInSystemCache(
IN PFCB Fcb,
IN PLARGE_INTEGER FileOffset OPTIONAL,
IN ULONG Length,
IN BOOLEAN UninitializeCacheMaps,
IN BOOLEAN FlushFile)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/*
* @implemented
*/
VOID
RxpWorkerThreadDispatcher(
IN PRX_WORK_QUEUE WorkQueue,
IN PLARGE_INTEGER WaitInterval)
{
NTSTATUS Status;
PVOID Parameter;
PETHREAD CurrentThread;
BOOLEAN KillThread, Dereference;
PRX_WORK_QUEUE_ITEM WorkQueueItem;
PWORKER_THREAD_ROUTINE WorkerRoutine;
InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
/* Reference ourselves */
CurrentThread = PsGetCurrentThread();
Status = ObReferenceObjectByPointer(CurrentThread, THREAD_ALL_ACCESS, PsThreadType, KernelMode);
ASSERT(NT_SUCCESS(Status));
/* Infinite loop for worker */
KillThread = FALSE;
Dereference = FALSE;
do
{
KIRQL OldIrql;
PLIST_ENTRY ListEntry;
/* Remove an entry from the work queue */
ListEntry = KeRemoveQueue(&WorkQueue->Queue, KernelMode, WaitInterval);
if ((ULONG_PTR)ListEntry != STATUS_TIMEOUT)
{
PRDBSS_DEVICE_OBJECT DeviceObject;
WorkQueueItem = CONTAINING_RECORD(ListEntry, RX_WORK_QUEUE_ITEM, List);
InterlockedIncrement(&WorkQueue->NumberOfWorkItemsDispatched);
InterlockedDecrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
/* Get the parameters, and null-them in the struct */
WorkerRoutine = WorkQueueItem->WorkerRoutine;
Parameter = WorkQueueItem->Parameter;
DeviceObject = WorkQueueItem->pDeviceObject;
WorkQueueItem->List.Flink = NULL;
WorkQueueItem->WorkerRoutine = NULL;
WorkQueueItem->Parameter = NULL;
WorkQueueItem->pDeviceObject = NULL;
/* Call the routine */
DPRINT("Calling: %p(%p)\n", WorkerRoutine, Parameter);
WorkerRoutine(Parameter);
/* Are we going down now? */
if (InterlockedDecrement(&DeviceObject->DispatcherContext.NumberOfWorkerThreads) == 0)
{
PKEVENT TearDownEvent;
TearDownEvent = InterlockedExchangePointer((volatile PVOID)&DeviceObject->DispatcherContext.pTearDownEvent, NULL);
if (TearDownEvent != NULL)
{
KeSetEvent(TearDownEvent, IO_NO_INCREMENT, FALSE);
}
}
InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
}
/* Shall we shutdown... */
KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
switch (WorkQueue->State)
{
/* Our queue is active, kill it if we have no more items to dispatch
* and more threads than the required minimum
*/
case RxWorkQueueActive:
if (WorkQueue->NumberOfWorkItemsToBeDispatched <= 0)
{
ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
{
KillThread = TRUE;
Dereference = TRUE;
InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
}
if (KillThread)
{
InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
}
}
break;
/* The queue is inactive: kill it we have more threads than the required minimum */
case RxWorkQueueInactive:
ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
{
KillThread = TRUE;
Dereference = TRUE;
InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
}
if (KillThread)
{
InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
}
break;
/* Rundown in progress..., kill it for sure! */
case RxWorkQueueRundownInProgress:
{
PRX_WORK_QUEUE_RUNDOWN_CONTEXT RundownContext;
ASSERT(WorkQueue->pRundownContext != NULL);
RundownContext = WorkQueue->pRundownContext;
RundownContext->ThreadPointers[RundownContext->NumberOfThreadsSpunDown++] = CurrentThread;
InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
KillThread = TRUE;
Dereference = FALSE;
if (WorkQueue->NumberOfActiveWorkerThreads == 0)
{
KeSetEvent(&RundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
}
InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
}
break;
default:
break;
}
KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
} while (!KillThread);
DPRINT("Killed worker thread\n");
/* Do we have to dereference ourselves? */
if (Dereference)
{
ObDereferenceObject(CurrentThread);
}
/* Dump last executed routine */
if (DumpDispatchRoutine)
{
DPRINT("Dispatch routine %p(%p) taken from %p\n", WorkerRoutine, Parameter, WorkQueueItem);
}
PsTerminateSystemThread(STATUS_SUCCESS);
}
VOID
RxReference(
IN OUT PVOID Instance)
{
NODE_TYPE_CODE NodeType;
PNODE_TYPE_AND_SIZE Node;
PAGED_CODE();
RxAcquireScavengerMutex();
/* We can only reference a few structs */
NodeType = NodeType(Instance) & ~RX_SCAVENGER_MASK;
ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
(NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
(NodeType == RDBSS_NTC_FOBX));
Node = (PNODE_TYPE_AND_SIZE)Instance;
InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
/* Trace refcount if asked */
switch (NodeType)
{
case RDBSS_NTC_SRVCALL:
PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
break;
case RDBSS_NTC_NETROOT:
PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
break;
case RDBSS_NTC_V_NETROOT:
PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
break;
case RDBSS_NTC_SRVOPEN:
PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
break;
case RDBSS_NTC_FOBX:
PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
break;
default:
ASSERT(FALSE);
break;
}
RxpUndoScavengerFinalizationMarking(Instance);
RxReleaseScavengerMutex();
}
VOID
NTAPI
RxResumeBlockedOperations_Serially(
IN OUT PRX_CONTEXT RxContext,
IN OUT PLIST_ENTRY BlockingIoQ)
{
PAGED_CODE();
RxAcquireSerializationMutex();
/* This can only happen on pipes */
if (!BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION))
{
RxReleaseSerializationMutex();
return;
}
UNIMPLEMENTED;
RxReleaseSerializationMutex();
}
BOOLEAN
RxScavengeRelatedFobxs(
PFCB Fcb)
{
UNIMPLEMENTED;
return FALSE;
}
BOOLEAN
RxScavengeVNetRoots(
PRDBSS_DEVICE_OBJECT RxDeviceObject)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
VOID
NTAPI
RxSpinUpRequestsDispatcher(
PVOID Dispatcher)
{
NTSTATUS Status;
PRX_DISPATCHER RxDispatcher;
Status = ObReferenceObjectByPointer(PsGetCurrentThread(), THREAD_ALL_ACCESS, PsThreadType, KernelMode);
if (!NT_SUCCESS(Status))
{
PsTerminateSystemThread(STATUS_SUCCESS);
}
RxDispatcher = Dispatcher;
do
{
KIRQL OldIrql;
PLIST_ENTRY ListEntry;
Status = KeWaitForSingleObject(&RxDispatcher->SpinUpRequestsEvent, Executive,
KernelMode, FALSE, &RxSpinUpDispatcherWaitInterval);
ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_TIMEOUT));
KeAcquireSpinLock(&RxDispatcher->SpinUpRequestsLock, &OldIrql);
if (!IsListEmpty(&RxDispatcher->SpinUpRequests))
{
ListEntry = RemoveHeadList(&RxDispatcher->SpinUpRequests);
}
else
{
ListEntry = &RxDispatcher->SpinUpRequests;
}
KeResetEvent(&RxDispatcher->SpinUpRequestsEvent);
KeReleaseSpinLock(&RxDispatcher->SpinUpRequestsLock, OldIrql);
while (ListEntry != &RxDispatcher->SpinUpRequests)
{
PWORK_QUEUE_ITEM WorkItem;
PRX_WORK_QUEUE WorkQueue;
WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List);
WorkQueue = WorkItem->Parameter;
InterlockedDecrement(&WorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
DPRINT1("WORKQ:SR %lx %lx\n", WorkItem->WorkerRoutine, WorkItem->Parameter);
WorkItem->WorkerRoutine(WorkItem->Parameter);
}
} while (RxDispatcher->State == RxDispatcherActive);
KeSetEvent(&RxDispatcher->SpinUpRequestsTearDownEvent, IO_NO_INCREMENT, FALSE);
PsTerminateSystemThread(STATUS_SUCCESS);
}
/*
* @implemented
*/
NTSTATUS
RxSpinUpWorkerThread(
PRX_WORK_QUEUE WorkQueue,
PRX_WORKERTHREAD_ROUTINE Routine,
PVOID Parameter)
{
KIRQL OldIrql;
NTSTATUS Status;
HANDLE ThreadHandle;
PAGED_CODE();
/* If work queue is inactive, that cannot work */
KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
if (WorkQueue->State != RxWorkQueueActive)
{
Status = STATUS_UNSUCCESSFUL;
DPRINT("Workqueue not active! WorkQ: %p, State: %d, Active: %d\n", WorkQueue, WorkQueue->State, WorkQueue->NumberOfActiveWorkerThreads);
}
else
{
++WorkQueue->NumberOfActiveWorkerThreads;
Status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
/* Quit on failure */
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Spin up the worker thread */
Status = PsCreateSystemThread(&ThreadHandle, PROCESS_ALL_ACCESS, NULL, NULL, NULL, Routine, Parameter);
if (NT_SUCCESS(Status))
{
ZwClose(ThreadHandle);
return Status;
}
/* Read well: we reached that point because it failed! */
DPRINT("WorkQ: %p, Status: %lx\n", WorkQueue, Status);
KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
--WorkQueue->NumberOfActiveWorkerThreads;
++WorkQueue->NumberOfFailedSpinUpRequests;
/* Rundown, no more active threads, set the event! */
if (WorkQueue->NumberOfActiveWorkerThreads == 0 &&
WorkQueue->State == RxWorkQueueRundownInProgress)
{
KeSetEvent(&WorkQueue->pRundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
}
DPRINT("Workqueue not active! WorkQ: %p, State: %d, Active: %d\n", WorkQueue, WorkQueue->State, WorkQueue->NumberOfActiveWorkerThreads);
KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
return Status;
}
VOID
RxSpinUpWorkerThreads(
PRX_WORK_QUEUE WorkQueue)
{
UNIMPLEMENTED;
}
VOID
RxSynchronizeWithScavenger(
IN PRX_CONTEXT RxContext)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
ULONG
RxTableComputeHashValue(
IN PUNICODE_STRING Name)
{
ULONG Hash;
SHORT Loops[8];
USHORT MaxChar, i;
PAGED_CODE();
MaxChar = Name->Length / sizeof(WCHAR);
Loops[0] = 1;
Loops[1] = MaxChar - 1;
Loops[2] = MaxChar - 2;
Loops[3] = MaxChar - 3;
Loops[4] = MaxChar - 4;
Loops[5] = MaxChar / 4;
Loops[6] = 2 * MaxChar / 4;
Loops[7] = 3 * MaxChar / 4;
Hash = 0;
for (i = 0; i < 8; ++i)
{
SHORT Idx;
Idx = Loops[i];
if (Idx >= 0 && Idx < MaxChar)
{
Hash = RtlUpcaseUnicodeChar(Name->Buffer[Idx]) + 8 * Hash;
}
}
return Hash;
}
/*
* @implemented
*/
ULONG
RxTableComputePathHashValue(
IN PUNICODE_STRING Name)
{
ULONG Hash;
SHORT Loops[8];
USHORT MaxChar, i;
PAGED_CODE();
MaxChar = Name->Length / sizeof(WCHAR);
Loops[0] = 1;
Loops[1] = MaxChar - 1;
Loops[2] = MaxChar - 2;
Loops[3] = MaxChar - 3;
Loops[4] = MaxChar - 4;
Loops[5] = MaxChar / 4;
Loops[6] = 2 * MaxChar / 4;
Loops[7] = 3 * MaxChar / 4;
Hash = 0;
for (i = 0; i < 8; ++i)
{
SHORT Idx;
Idx = Loops[i];
if (Idx >= 0 && Idx < MaxChar)
{
Hash = RtlUpcaseUnicodeChar(Name->Buffer[Idx]) + 8 * Hash;
}
}
return Hash;
}
PVOID
RxTableLookupName(
IN PRX_PREFIX_TABLE ThisTable,
IN PUNICODE_STRING Name,
OUT PUNICODE_STRING RemainingName,
IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
{
PVOID Container;
USHORT i, MaxChar;
PRX_PREFIX_ENTRY Entry;
RX_CONNECTION_ID NullId;
UNICODE_STRING LookupString;
PAGED_CODE();
/* If caller didn't provide a connection ID, setup one */
if (ThisTable->IsNetNameTable && RxConnectionId == NULL)
{
NullId.Luid.LowPart = 0;
NullId.Luid.HighPart = 0;
RxConnectionId = &NullId;
}
/* Validate name */
ASSERT(Name->Buffer[0] == OBJ_NAME_PATH_SEPARATOR);
Entry = NULL;
Container = NULL;
LookupString.Buffer = Name->Buffer;
MaxChar = Name->Length / sizeof(WCHAR);
/* We'll perform the lookup, path component after another */
for (i = 1; i < MaxChar; ++i)
{
ULONG Hash;
PRX_PREFIX_ENTRY CurEntry;
/* Don't cut in the middle of a path element */
if (Name->Buffer[i] != OBJ_NAME_PATH_SEPARATOR && Name->Buffer[i] != ':')
{
continue;
}
/* Perform lookup in the table */
LookupString.Length = i * sizeof(WCHAR);
Hash = RxTableComputeHashValue(&LookupString);
CurEntry = RxTableLookupName_ExactLengthMatch(ThisTable, &LookupString, Hash, RxConnectionId);
#if DBG
++ThisTable->Lookups;
#endif
/* Entry not found, move to the next component */
if (CurEntry == NULL)
{
#if DBG
++ThisTable->FailedLookups;
#endif
continue;
}
Entry = CurEntry;
ASSERT(Entry->ContainingRecord != NULL);
Container = Entry->ContainingRecord;
/* Need to handle NetRoot specific case... */
if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_NETROOT)
{
UNIMPLEMENTED;
break;
}
else if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_V_NETROOT)
{
break;
}
else
{
ASSERT((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_SRVCALL);
}
}
/* Entry was found */
if (Entry != NULL)
{
DPRINT("Found\n");
ASSERT(Name->Length >= Entry->Prefix.Length);
/* Setup remaining name */
RemainingName->Buffer = Add2Ptr(Name->Buffer, Entry->Prefix.Length);
RemainingName->Length = Name->Length - Entry->Prefix.Length;
RemainingName->MaximumLength = Name->Length - Entry->Prefix.Length;
}
else
{
/* Otherwise, that's the whole name */
RemainingName = Name;
}
return Container;
}
/*
* @implemented
*/
PRX_PREFIX_ENTRY
RxTableLookupName_ExactLengthMatch(
IN PRX_PREFIX_TABLE ThisTable,
IN PUNICODE_STRING Name,
IN ULONG HashValue,
IN PRX_CONNECTION_ID OPTIONAL RxConnectionId)
{
PLIST_ENTRY ListEntry, HashBucket;
PAGED_CODE();
ASSERT(RxConnectionId != NULL);
/* Select the right bucket */
HashBucket = HASH_BUCKET(ThisTable, HashValue);
DPRINT("Looking in bucket: %p for %x\n", HashBucket, HashValue);
/* If bucket is empty, no match */
if (IsListEmpty(HashBucket))
{
return NULL;
}
/* Browse all the entries in the bucket */
for (ListEntry = HashBucket->Flink;
ListEntry != HashBucket;
ListEntry = ListEntry->Flink)
{
PVOID Container;
PRX_PREFIX_ENTRY Entry;
BOOLEAN CaseInsensitive;
PUNICODE_STRING CmpName, CmpPrefix;
UNICODE_STRING InsensitiveName, InsensitivePrefix;
Entry = CONTAINING_RECORD(ListEntry, RX_PREFIX_ENTRY, HashLinks);
++ThisTable->Considers;
ASSERT(HashBucket == HASH_BUCKET(ThisTable, Entry->SavedHashValue));
Container = Entry->ContainingRecord;
ASSERT(Container != NULL);
/* Not the same hash, not the same length, move on */
if (Entry->SavedHashValue != HashValue || Entry->Prefix.Length != Name->Length)
{
continue;
}
++ThisTable->Compares;
/* If we have to perform a case insensitive compare on a portion... */
if (Entry->CaseInsensitiveLength != 0)
{
ASSERT(Entry->CaseInsensitiveLength <= Name->Length);
/* Perform the case insensitive check on the asked length */
InsensitiveName.Buffer = Name->Buffer;
InsensitivePrefix.Buffer = Entry->Prefix.Buffer;
InsensitiveName.Length = Entry->CaseInsensitiveLength;
InsensitivePrefix.Length = Entry->CaseInsensitiveLength;
/* No match, move to the next entry */
if (!RtlEqualUnicodeString(&InsensitiveName, &InsensitivePrefix, TRUE))
{
continue;
}
/* Was the case insensitive covering the whole name? */
if (Name->Length == Entry->CaseInsensitiveLength)
{
/* If connection ID also matches, that a complete match! */
if (!ThisTable->IsNetNameTable || RxEqualConnectionId(RxConnectionId, &Entry->ConnectionId))
{
return Entry;
}
}
/* Otherwise, we have to continue with the sensitive match.... */
InsensitiveName.Buffer = Add2Ptr(InsensitiveName.Buffer, Entry->CaseInsensitiveLength);
InsensitivePrefix.Buffer = Add2Ptr(InsensitivePrefix.Buffer, Entry->CaseInsensitiveLength);
InsensitiveName.Length = Name->Length - Entry->CaseInsensitiveLength;
InsensitivePrefix.Length = Entry->Prefix.Length - Entry->CaseInsensitiveLength;
CmpName = &InsensitiveName;
CmpPrefix = &InsensitivePrefix;
CaseInsensitive = FALSE;
}
else
{
CmpName = Name;
CmpPrefix = &Entry->Prefix;
CaseInsensitive = ThisTable->CaseInsensitiveMatch;
}
/* Perform the compare, if there's a match, also check for connection ID */
if (RtlEqualUnicodeString(CmpName, CmpPrefix, CaseInsensitive))
{
if (!ThisTable->IsNetNameTable || RxEqualConnectionId(RxConnectionId, &Entry->ConnectionId))
{
return Entry;
}
}
}
return NULL;
}
/*
* @implemented
*/
VOID
RxTrackerUpdateHistory(
_Inout_opt_ PRX_CONTEXT RxContext,
_Inout_ PMRX_FCB MrxFcb,
_In_ ULONG Operation,
_In_ ULONG LineNumber,
_In_ PCSTR FileName,
_In_ ULONG SerialNumber)
{
PFCB Fcb;
RX_FCBTRACKER_CASES Case;
/* Check for null or special context */
if (RxContext == NULL)
{
Case = RX_FCBTRACKER_CASE_NULLCONTEXT;
}
else if ((ULONG_PTR)RxContext == -1)
{
Case = RX_FCBTRACKER_CASE_CBS_CONTEXT;
}
else if ((ULONG_PTR)RxContext == -2)
{
Case = RX_FCBTRACKER_CASE_CBS_WAIT_CONTEXT;
}
else
{
ASSERT(NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT);
Case = RX_FCBTRACKER_CASE_NORMAL;
}
/* If caller provided a FCB, update its history */
if (MrxFcb != NULL)
{
Fcb = (PFCB)MrxFcb;
ASSERT(NodeTypeIsFcb(Fcb));
/* Only one acquire operation, so many release operations... */
if (Operation == TRACKER_ACQUIRE_FCB)
{
++Fcb->FcbAcquires[Case];
}
else
{
++Fcb->FcbReleases[Case];
}
}
/* If we have a normal context, update its history about this function calls */
if (Case == RX_FCBTRACKER_CASE_NORMAL)
{
ULONG TrackerHistoryPointer;
/* Only one acquire operation, so many release operations... */
if (Operation == TRACKER_ACQUIRE_FCB)
{
InterlockedIncrement(&RxContext->AcquireReleaseFcbTrackerX);
}
else
{
InterlockedDecrement(&RxContext->AcquireReleaseFcbTrackerX);
}
/* We only keep track of the 32 first calls */
TrackerHistoryPointer = InterlockedExchangeAdd((volatile long *)&RxContext->TrackerHistoryPointer, 1);
if (TrackerHistoryPointer < RDBSS_TRACKER_HISTORY_SIZE)
{
RxContext->TrackerHistory[TrackerHistoryPointer].AcquireRelease = Operation;
RxContext->TrackerHistory[TrackerHistoryPointer].LineNumber = LineNumber;
RxContext->TrackerHistory[TrackerHistoryPointer].FileName = (PSZ)FileName;
RxContext->TrackerHistory[TrackerHistoryPointer].SavedTrackerValue = RxContext->AcquireReleaseFcbTrackerX;
RxContext->TrackerHistory[TrackerHistoryPointer].Flags = RxContext->Flags;
}
/* If it's negative, then we released once more than we acquired it?! */
ASSERT(RxContext->AcquireReleaseFcbTrackerX >= 0);
}
}
VOID
RxTrackPagingIoResource(
_Inout_ PVOID Instance,
_In_ ULONG Type,
_In_ ULONG Line,
_In_ PCSTR File)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
VOID
RxUninitializeVNetRootParameters(
IN PUNICODE_STRING UserName,
IN PUNICODE_STRING UserDomainName,
IN PUNICODE_STRING Password,
OUT PULONG Flags)
{
PAGED_CODE();
/* Only free what could have been allocated */
if (UserName != NULL)
{
ExFreePool(UserName);
}
if (UserDomainName != NULL)
{
ExFreePool(UserDomainName);
}
if (Password != NULL)
{
ExFreePool(Password);
}
/* And remove the possibly set CSC agent flag */
if (Flags != NULL)
{
(*Flags) &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE;
}
}
/*
* @implemented
*/
VOID
RxUpdateCondition(
IN RX_BLOCK_CONDITION NewConditionValue,
OUT PRX_BLOCK_CONDITION Condition,
IN OUT PLIST_ENTRY TransitionWaitList)
{
PRX_CONTEXT Context;
LIST_ENTRY SerializationQueue;
PAGED_CODE();
DPRINT("RxUpdateCondition(%d, %p, %p)\n", NewConditionValue, Condition, TransitionWaitList);
/* Set the new condition */
RxAcquireSerializationMutex();
ASSERT(NewConditionValue != Condition_InTransition);
*Condition = NewConditionValue;
/* And get the serialization queue for treatment */
RxTransferList(&SerializationQueue, TransitionWaitList);
RxReleaseSerializationMutex();
/* Handle the serialization queue */
Context = RxRemoveFirstContextFromSerializationQueue(&SerializationQueue);
while (Context != NULL)
{
/* If the caller asked for post, post the request */
if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION))
{
Context->Flags &= ~RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION;
RxFsdPostRequest(Context);
}
/* Otherwise, wake up sleeping waiters */
else
{
RxSignalSynchronousWaiter(Context);
}
Context = RxRemoveFirstContextFromSerializationQueue(&SerializationQueue);
}
}
VOID
RxVerifyOperationIsLegal(
IN PRX_CONTEXT RxContext)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
VOID
RxWaitForStableCondition(
IN PRX_BLOCK_CONDITION Condition,
IN OUT PLIST_ENTRY TransitionWaitList,
IN OUT PRX_CONTEXT RxContext,
OUT NTSTATUS *AsyncStatus OPTIONAL)
{
BOOLEAN Wait;
NTSTATUS LocalStatus;
PAGED_CODE();
/* Make sure to always get status */
if (AsyncStatus == NULL)
{
AsyncStatus = &LocalStatus;
}
/* By default, it's a success */
*AsyncStatus = STATUS_SUCCESS;
Wait = FALSE;
/* If it's not stable, we've to wait */
if (!StableCondition(*Condition))
{
/* Lock the mutex */
RxAcquireSerializationMutex();
/* Still not stable? */
if (!StableCondition(*Condition))
{
/* Insert us in the wait list for processing on stable condition */
RxInsertContextInSerializationQueue(TransitionWaitList, RxContext);
/* If we're asked to post on stable, don't wait, and just return pending */
if (BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION))
{
*AsyncStatus = STATUS_PENDING;
}
else
{
Wait = TRUE;
}
}
RxReleaseSerializationMutex();
/* We don't post on stable, so, just wait... */
if (Wait)
{
RxWaitSync(RxContext);
}
}
}
/*
* @implemented
*/
VOID
NTAPI
RxWorkItemDispatcher(
PVOID Context)
{
PRX_WORK_DISPATCH_ITEM DispatchItem = Context;
DPRINT("Calling: %p, %p\n", DispatchItem->DispatchRoutine, DispatchItem->DispatchRoutineParameter);
DispatchItem->DispatchRoutine(DispatchItem->DispatchRoutineParameter);
ExFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
}
NTSTATUS
__RxAcquireFcb(
_Inout_ PFCB Fcb,
_Inout_opt_ PRX_CONTEXT RxContext OPTIONAL,
_In_ ULONG Mode
#ifdef RDBSS_TRACKER
,
_In_ ULONG LineNumber,
_In_ PCSTR FileName,
_In_ ULONG SerialNumber
#endif
)
{
NTSTATUS Status;
BOOLEAN SpecialContext, CanWait, Acquired, ContextIsPresent;
PAGED_CODE();
DPRINT("__RxAcquireFcb(%p, %p, %d, %d, %s, %d)\n", Fcb, RxContext, Mode, LineNumber, FileName, SerialNumber);
SpecialContext = FALSE;
ContextIsPresent = FALSE;
/* Check for special context */
if ((ULONG_PTR)RxContext == -1 || (ULONG_PTR)RxContext == -2)
{
SpecialContext = TRUE;
}
/* We don't handle buffering state change yet... */
if (!RxIsFcbAcquired(Fcb) && !SpecialContext &&
BooleanFlagOn(Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING))
{
UNIMPLEMENTED;
}
/* Nor special contexts */
if (SpecialContext)
{
UNIMPLEMENTED;
}
/* Nor missing contexts... */
if (RxContext == NULL)
{
UNIMPLEMENTED;
}
/* That said: we have a real context! */
ContextIsPresent = TRUE;
/* If we've been cancelled in between, give up */
Status = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED) ? STATUS_CANCELLED : STATUS_SUCCESS;
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Can we wait? */
CanWait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
while (TRUE)
{
/* Assume we cannot lock */
Status = STATUS_LOCK_NOT_GRANTED;
/* Lock according to what the caller asked */
switch (Mode)
{
case FCB_MODE_EXCLUSIVE:
Acquired = ExAcquireResourceExclusiveLite(Fcb->Header.Resource, CanWait);
break;
case FCB_MODE_SHARED:
Acquired = ExAcquireResourceSharedLite(Fcb->Header.Resource, CanWait);
break;
case FCB_MODE_SHARED_WAIT_FOR_EXCLUSIVE:
Acquired = ExAcquireSharedWaitForExclusive(Fcb->Header.Resource, CanWait);
break;
default:
ASSERT(Mode == FCB_MODE_SHARED_STARVE_EXCLUSIVE);
Acquired = ExAcquireSharedStarveExclusive(Fcb->Header.Resource, CanWait);
break;
}
/* Lock granted! */
if (Acquired)
{
Status = STATUS_SUCCESS;
ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
/* Handle paging write - not implemented */
if (Fcb->NonPaged->OutstandingAsyncWrites != 0)
{
UNIMPLEMENTED;
}
}
/* And break, that cool! */
if (Acquired)
{
break;
}
/* If it failed, return immediately */
if (!NT_SUCCESS(Status))
{
return Status;
}
}
/* If we don't have to check for valid operation, job done, nothing more to do */
if (!ContextIsPresent || BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK))
{
if (NT_SUCCESS(Status))
{
RxTrackerUpdateHistory(RxContext, RX_GET_MRX_FCB(Fcb), TRACKER_ACQUIRE_FCB, LineNumber, FileName, SerialNumber);
}
return Status;
}
/* Verify operation */
_SEH2_TRY
{
RxVerifyOperationIsLegal(RxContext);
}
_SEH2_FINALLY
{
/* If it failed, release lock and fail */
if (_SEH2_AbnormalTermination())
{
ExReleaseResourceLite(Fcb->Header.Resource);
Status = STATUS_LOCK_NOT_GRANTED;
}
}
_SEH2_END;
if (NT_SUCCESS(Status))
{
RxTrackerUpdateHistory(RxContext, RX_GET_MRX_FCB(Fcb), TRACKER_ACQUIRE_FCB, LineNumber, FileName, SerialNumber);
}
DPRINT("Status: %x\n", Status);
return Status;
}
/*
* @implemented
*/
VOID
__RxItsTheSameContext(
_In_ PRX_CONTEXT RxContext,
_In_ ULONG CapturedRxContextSerialNumber,
_In_ ULONG Line,
_In_ PCSTR File)
{
/* Check we have a context with the same serial number */
if (NodeType(RxContext) != RDBSS_NTC_RX_CONTEXT ||
RxContext->SerialNumber != CapturedRxContextSerialNumber)
{
/* Just be noisy */
DPRINT1("Context %p has changed at line %d in file %s\n", RxContext, Line, File);
}
}
VOID
__RxReleaseFcb(
_Inout_opt_ PRX_CONTEXT RxContext,
_Inout_ PMRX_FCB MrxFcb
#ifdef RDBSS_TRACKER
,
_In_ ULONG LineNumber,
_In_ PCSTR FileName,
_In_ ULONG SerialNumber
#endif
)
{
BOOLEAN IsExclusive, BufferingPending;
RxAcquireSerializationMutex();
BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
IsExclusive = BooleanFlagOn(MrxFcb->Header.Resource->Flag, ResourceOwnedExclusive);
/* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
* then just release the FCB
*/
if (!BufferingPending || !IsExclusive)
{
RxTrackerUpdateHistory(RxContext, MrxFcb, (!BufferingPending ? TRACKER_RELEASE_FCB_NO_BUFF_PENDING : TRACKER_RELEASE_NON_EXCL_FCB_BUFF_PENDING),
LineNumber, FileName, SerialNumber);
ExReleaseResourceLite(MrxFcb->Header.Resource);
}
RxReleaseSerializationMutex();
/* And finally leave */
if (!BufferingPending || !IsExclusive)
{
return;
}
ASSERT(RxIsFcbAcquiredExclusive(MrxFcb));
/* Otherwise, handle buffering state and release */
RxProcessFcbChangeBufferingStateRequest((PFCB)MrxFcb);
RxTrackerUpdateHistory(RxContext, MrxFcb, TRACKER_RELEASE_EXCL_FCB_BUFF_PENDING, LineNumber, FileName, SerialNumber);
ExReleaseResourceLite(MrxFcb->Header.Resource);
}
VOID
__RxReleaseFcbForThread(
_Inout_opt_ PRX_CONTEXT RxContext,
_Inout_ PMRX_FCB MrxFcb,
_In_ ERESOURCE_THREAD ResourceThreadId
#ifdef RDBSS_TRACKER
,
_In_ ULONG LineNumber,
_In_ PCSTR FileName,
_In_ ULONG SerialNumber
#endif
)
{
BOOLEAN IsExclusive, BufferingPending;
RxAcquireSerializationMutex();
BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
IsExclusive = BooleanFlagOn(MrxFcb->Header.Resource->Flag, ResourceOwnedExclusive);
/* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
* then just release the FCB
*/
if (!BufferingPending || !IsExclusive)
{
RxTrackerUpdateHistory(RxContext, MrxFcb,
(!BufferingPending ? TRACKER_RELEASE_FCB_FOR_THRD_NO_BUFF_PENDING : TRACKER_RELEASE_NON_EXCL_FCB_FOR_THRD_BUFF_PENDING),
LineNumber, FileName, SerialNumber);
ExReleaseResourceForThreadLite(MrxFcb->Header.Resource, ResourceThreadId);
}
RxReleaseSerializationMutex();
/* And finally leave */
if (!BufferingPending || !IsExclusive)
{
return;
}
/* Otherwise, handle buffering state and release */
RxTrackerUpdateHistory(RxContext, MrxFcb, TRACKER_RELEASE_EXCL_FCB_FOR_THRD_BUFF_PENDING, LineNumber, FileName, SerialNumber);
RxProcessFcbChangeBufferingStateRequest((PFCB)MrxFcb);
ExReleaseResourceForThreadLite(MrxFcb->Header.Resource, ResourceThreadId);
}