1170 lines
35 KiB
C
1170 lines
35 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
RxContx.c
|
||
|
||
Abstract:
|
||
|
||
This module implements routine to allocate/initialize and to delete an Irp
|
||
Context. These structures are very important because they link Irps with the
|
||
RDBSS. They encapsulate all the context required to process an IRP.
|
||
|
||
Author:
|
||
|
||
Joe Linn [JoeLinn] 21-aug-1994
|
||
|
||
Revision History:
|
||
|
||
Balan Sethu Raman [SethuR] 07-June-1995 Included support for cancelling
|
||
requests
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#include <dfsfsctl.h>
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, RxInitializeContext)
|
||
#pragma alloc_text(PAGE, RxReinitializeContext)
|
||
#pragma alloc_text(PAGE, RxPrepareContextForReuse)
|
||
#pragma alloc_text(PAGE, RxCompleteRequest)
|
||
#pragma alloc_text(PAGE, __RxSynchronizeBlockingOperationsMaybeDroppingFcbLock)
|
||
#pragma alloc_text(PAGE, RxResumeBlockedOperations_Serially)
|
||
#pragma alloc_text(PAGE, RxResumeBlockedOperations_ALL)
|
||
#pragma alloc_text(PAGE, RxCancelBlockingOperation)
|
||
#pragma alloc_text(PAGE, RxRemoveOperationFromBlockingQueue)
|
||
#endif
|
||
|
||
BOOLEAN RxSmallContextLogEntry = FALSE;
|
||
|
||
FAST_MUTEX RxContextPerFileSerializationMutex;
|
||
|
||
//
|
||
// The debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_RXCONTX)
|
||
|
||
ULONG RxContextSerialNumberCounter = 0;
|
||
|
||
#ifdef RDBSSLOG
|
||
//this stuff must be in nonpaged memory
|
||
//// 1 2 3 4 5 6 7 8 9
|
||
char RxInitContext_SurrogateFormat[] = "%S%S%N%N%N%N%N%N%N";
|
||
//// 2 3 4 5 6 7 8 9
|
||
char RxInitContext_ActualFormat[] = "Irp++ %s/%lx %08lx irp %lx thrd %lx %lx:%lx #%lx";
|
||
|
||
#endif //ifdef RDBSSLOG
|
||
|
||
VOID
|
||
ValidateBlockingIoQ(
|
||
PLIST_ENTRY BlockingIoQ
|
||
);
|
||
|
||
|
||
VOID
|
||
RxInitializeContext(
|
||
IN PIRP Irp,
|
||
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
|
||
IN ULONG InitialContextFlags,
|
||
IN OUT PRX_CONTEXT RxContext)
|
||
{
|
||
PDEVICE_OBJECT TopLevelDeviceObject;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace(+1, Dbg, ("RxInitializeContext\n"));
|
||
|
||
// some asserts that we need. This ensures that the two values that are
|
||
// packaged together as an IoStatusBlock can be manipulated independently
|
||
// as well as together.
|
||
|
||
ASSERT(
|
||
FIELD_OFFSET(RX_CONTEXT,StoredStatus) ==
|
||
FIELD_OFFSET(RX_CONTEXT,IoStatusBlock.Status));
|
||
|
||
ASSERT(
|
||
FIELD_OFFSET(RX_CONTEXT,InformationToReturn) ==
|
||
FIELD_OFFSET(RX_CONTEXT,IoStatusBlock.Information));
|
||
|
||
// Set the proper node type code, node byte size and the flags
|
||
RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT;
|
||
RxContext->NodeByteSize = sizeof(RX_CONTEXT);
|
||
RxContext->ReferenceCount = 1;
|
||
RxContext->SerialNumber = InterlockedIncrement(&RxContextSerialNumberCounter);
|
||
RxContext->RxDeviceObject = RxDeviceObject;
|
||
|
||
// Initialize the Sync Event.
|
||
KeInitializeEvent(
|
||
&RxContext->SyncEvent,
|
||
SynchronizationEvent,
|
||
FALSE);
|
||
|
||
// Initialize the associated scavenger entry
|
||
RxInitializeScavengerEntry(&RxContext->ScavengerEntry);
|
||
|
||
// Initialize the list entry of blocked operations
|
||
InitializeListHead(&RxContext->BlockedOperations);
|
||
|
||
RxContext->MRxCancelRoutine = NULL;
|
||
RxContext->ResumeRoutine = NULL;
|
||
|
||
SetFlag( RxContext->Flags, InitialContextFlags);
|
||
|
||
// Set the Irp fields....for cacheing and hiding
|
||
RxContext->CurrentIrp = Irp;
|
||
RxContext->OriginalThread = RxContext->LastExecutionThread = PsGetCurrentThread();
|
||
|
||
if (Irp != NULL) {
|
||
PIO_STACK_LOCATION IrpSp;
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp ); //ok4ioget
|
||
|
||
// There are certain operations that are open ended in the redirector.
|
||
// The change notification mechanism is one of them. On a synchronous
|
||
// operation if the wait is in the redirector then we will not be able
|
||
// to cancel because FsRtlEnterFileSystem disables APC's. Therefore
|
||
// we convert the synchronous operation into an asynchronous one and
|
||
// let the I/O system do the waiting.
|
||
|
||
if (IrpSp->FileObject != NULL) {
|
||
if (!IoIsOperationSynchronous(Irp)) {
|
||
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
||
} else {
|
||
PFCB Fcb;
|
||
|
||
Fcb = (PFCB)IrpSp->FileObject->FsContext;
|
||
|
||
if ((Fcb != NULL) && NodeTypeIsFcb(Fcb)) {
|
||
if (((IrpSp->MajorFunction == IRP_MJ_READ) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_WRITE) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL)) &&
|
||
(Fcb->pNetRoot != NULL) &&
|
||
(Fcb->pNetRoot->Type == NET_ROOT_PIPE)) {
|
||
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((IrpSp->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
|
||
(IrpSp->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) {
|
||
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
||
}
|
||
|
||
//
|
||
// JOYC: make all device io control async
|
||
//
|
||
if (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
||
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
||
}
|
||
|
||
|
||
// Set the recursive file system call parameter. We set it true if
|
||
// the TopLevelIrp field in the thread local storage is not the current
|
||
// irp, otherwise we leave it as FALSE.
|
||
if ( !RxIsThisTheTopLevelIrp(Irp) ) {
|
||
SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_RECURSIVE_CALL);
|
||
}
|
||
if ( RxGetTopDeviceObjectIfRdbssIrp() == RxDeviceObject ) {
|
||
SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_THIS_DEVICE_TOP_LEVEL);
|
||
}
|
||
|
||
// Major/Minor Function codes
|
||
RxContext->MajorFunction = IrpSp->MajorFunction;
|
||
RxContext->MinorFunction = IrpSp->MinorFunction;
|
||
ASSERT(RxContext->MajorFunction<=IRP_MJ_MAXIMUM_FUNCTION);
|
||
|
||
RxContext->CurrentIrpSp = IrpSp;
|
||
|
||
if (IrpSp->FileObject) {
|
||
PFOBX Fobx;
|
||
PFCB Fcb;
|
||
|
||
Fcb = (PFCB)IrpSp->FileObject->FsContext;
|
||
Fobx = (PFOBX)IrpSp->FileObject->FsContext2;
|
||
|
||
RxContext->pFcb = (PMRX_FCB)Fcb;
|
||
if (Fcb && NodeTypeIsFcb(Fcb)) {
|
||
RxContext->NonPagedFcb = Fcb->NonPaged;
|
||
}
|
||
|
||
if (Fobx &&
|
||
(Fobx != (PFOBX)UIntToPtr(DFS_OPEN_CONTEXT)) &&
|
||
(Fobx != (PFOBX)UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT))) {
|
||
|
||
RxContext->pFobx = (PMRX_FOBX)Fobx;
|
||
RxContext->pRelevantSrvOpen = Fobx->pSrvOpen;
|
||
if (NodeType(Fobx)==RDBSS_NTC_FOBX) {
|
||
RxContext->FobxSerialNumber = InterlockedIncrement(&Fobx->FobxSerialNumber);
|
||
}
|
||
} else {
|
||
RxContext->pFobx = NULL;
|
||
}
|
||
|
||
// Copy IRP specific parameters.
|
||
if ((RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
|
||
(RxContext->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) {
|
||
|
||
if (Fobx != NULL) {
|
||
if (NodeType(Fobx)==RDBSS_NTC_FOBX) {
|
||
RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)Fcb->VNetRoot;
|
||
} else if (NodeType(Fobx) == RDBSS_NTC_V_NETROOT) {
|
||
RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)Fobx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Copy RealDevice for workque algorithms,
|
||
RxContext->RealDevice = IrpSp->FileObject->DeviceObject;
|
||
if (FlagOn(IrpSp->FileObject->Flags,FO_WRITE_THROUGH)){
|
||
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH );
|
||
}
|
||
}
|
||
} else {
|
||
RxContext->CurrentIrpSp = NULL;
|
||
// Major/Minor Function codes
|
||
RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1;
|
||
RxContext->MinorFunction = 0;
|
||
}
|
||
|
||
if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
|
||
PETHREAD Thread = PsGetCurrentThread();
|
||
UCHAR Pad = 0;
|
||
|
||
RxLog(
|
||
(
|
||
RxInitContext_SurrogateFormat,
|
||
RxInitContext_ActualFormat,
|
||
RXCONTX_OPERATION_NAME(
|
||
RxContext->MajorFunction,
|
||
BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WAIT)),
|
||
RxContext->MinorFunction,
|
||
RxContext,
|
||
Irp,
|
||
Thread,
|
||
RxContext->pFcb,
|
||
RxContext->pFobx,
|
||
RxContext->SerialNumber
|
||
));
|
||
|
||
RxWmiLog(LOG,
|
||
RxInitializeContext,
|
||
LOGPTR(RxContext)
|
||
LOGPTR(Irp)
|
||
LOGPTR(Thread)
|
||
LOGPTR(RxContext->pFcb)
|
||
LOGPTR(RxContext->pFobx)
|
||
LOGULONG(RxContext->SerialNumber)
|
||
LOGUCHAR(RxContext->MinorFunction)
|
||
LOGARSTR(RXCONTX_OPERATION_NAME(
|
||
RxContext->MajorFunction,
|
||
BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WAIT))));
|
||
}
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxInitializeContext -> %08lx %08lx %08lx\n",
|
||
RxContext,RxContext->pFcb,RxContext->pFobx));
|
||
}
|
||
|
||
PRX_CONTEXT
|
||
RxCreateRxContext (
|
||
IN PIRP Irp,
|
||
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
|
||
IN ULONG InitialContextFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new RX_CONTEXT record
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the originating Irp.
|
||
|
||
RxDeviceObject - the deviceobject that applies
|
||
|
||
InitialContextFlags - Supplies the wait value to store in the context;
|
||
also, the must_succeed value
|
||
|
||
Return Value:
|
||
|
||
PRX_CONTEXT - returns a pointer to the newly allocate RX_CONTEXT Record
|
||
|
||
--*/
|
||
{
|
||
KIRQL SavedIrql;
|
||
PRX_CONTEXT RxContext = NULL;
|
||
ULONG RxContextFlags = 0;
|
||
UCHAR MustSucceedDescriptorNumber = 0;
|
||
|
||
DebugDoit( InterlockedIncrement(&RxFsdEntryCount); )
|
||
|
||
ASSERT(RxDeviceObject!=NULL);
|
||
|
||
InterlockedIncrement(&RxDeviceObject->NumberOfActiveContexts);
|
||
|
||
if (RxContext == NULL) {
|
||
RxContext = ExAllocateFromNPagedLookasideList(
|
||
&RxContextLookasideList);
|
||
|
||
if (RxContext != NULL) {
|
||
SetFlag( RxContextFlags, RX_CONTEXT_FLAG_FROM_POOL );
|
||
}
|
||
}
|
||
|
||
if(RxContext == NULL){
|
||
return(NULL);
|
||
}
|
||
|
||
RtlZeroMemory( RxContext, sizeof(RX_CONTEXT) );
|
||
|
||
RxContext->Flags = RxContextFlags;
|
||
RxContext->MustSucceedDescriptorNumber = MustSucceedDescriptorNumber;
|
||
|
||
RxInitializeContext(Irp,RxDeviceObject,InitialContextFlags,RxContext);
|
||
|
||
ASSERT(
|
||
(RxContext->MajorFunction!=IRP_MJ_CREATE) ||
|
||
!FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED) );
|
||
|
||
KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql );
|
||
|
||
InsertTailList(&RxActiveContexts,&RxContext->ContextListEntry);
|
||
|
||
KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql );
|
||
|
||
return RxContext;
|
||
}
|
||
|
||
VOID
|
||
RxReinitializeContext(
|
||
IN OUT PRX_CONTEXT RxContext
|
||
)
|
||
{
|
||
PIRP Irp = RxContext->CurrentIrp;
|
||
PRDBSS_DEVICE_OBJECT RxDeviceObject = RxContext->RxDeviceObject;
|
||
|
||
ULONG PreservedContextFlags = RxContext->Flags & RX_CONTEXT_PRESERVED_FLAGS;
|
||
ULONG InitialContextFlags = RxContext->Flags & RX_CONTEXT_INITIALIZATION_FLAGS;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxPrepareContextForReuse(RxContext);
|
||
|
||
RtlZeroMemory(
|
||
(PCHAR)(&RxContext->ContextListEntry + 1),
|
||
sizeof(RX_CONTEXT) - FIELD_OFFSET(RX_CONTEXT,MajorFunction));
|
||
|
||
RxContext->Flags = PreservedContextFlags;
|
||
|
||
RxInitializeContext(Irp,RxDeviceObject,InitialContextFlags,RxContext);
|
||
}
|
||
|
||
VOID
|
||
RxPrepareContextForReuse(
|
||
IN OUT PRX_CONTEXT RxContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prepares a context for reuse by resetting all operation specific
|
||
allocations/acquistions that have been made. The parameters that have been
|
||
obtained from the IRP are not modified.
|
||
|
||
Arguments:
|
||
|
||
RxContext - Supplies the RX_CONTEXT to remove
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Clean up the operation specific stuff
|
||
switch (RxContext->MajorFunction) {
|
||
|
||
case IRP_MJ_CREATE:
|
||
ASSERT ( RxContext->Create.CanonicalNameBuffer == NULL );
|
||
break;
|
||
|
||
case IRP_MJ_READ :
|
||
case IRP_MJ_WRITE :
|
||
{
|
||
ASSERT(RxContext->RxContextSerializationQLinks.Flink == NULL);
|
||
ASSERT(RxContext->RxContextSerializationQLinks.Blink == NULL);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
NOTHING;
|
||
}
|
||
|
||
RxContext->ReferenceCount = 0;
|
||
}
|
||
|
||
|
||
VOID
|
||
RxDereferenceAndDeleteRxContext_Real (
|
||
IN PRX_CONTEXT RxContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences an RxContexts and if the refcount goes to zero
|
||
then it deallocates and removes the specified RX_CONTEXT record from the
|
||
Rx in-memory data structures. IT is called by routines other than
|
||
RxCompleteRequest async requests touch the RxContext "last" in either the
|
||
initiating thread or in some other thread. Thus, we refcount the structure
|
||
and finalize on the last dereference.
|
||
|
||
Arguments:
|
||
|
||
RxContext - Supplies the RX_CONTEXT to remove
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
KIRQL SavedIrql;
|
||
BOOLEAN RxContextIsFromPool;
|
||
BOOLEAN RxContextIsMustSucceedAllocated;
|
||
BOOLEAN RxContextIsFromZone;
|
||
PRDBSS_DEVICE_OBJECT RxDeviceObject;
|
||
PRX_CONTEXT pStopContext = NULL;
|
||
LONG FinalRefCount;
|
||
|
||
RxDbgTraceLV(+1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext, RxContext = %08lx (%lu)\n",
|
||
RxContext,RxContext->SerialNumber));
|
||
|
||
KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql );
|
||
|
||
ASSERT( RxContext->NodeTypeCode == RDBSS_NTC_RX_CONTEXT );
|
||
|
||
FinalRefCount = InterlockedDecrement(&RxContext->ReferenceCount);
|
||
|
||
if (FinalRefCount == 0) {
|
||
RxDeviceObject = RxContext->RxDeviceObject;
|
||
RxContextIsFromPool = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_FROM_POOL);
|
||
RxContextIsMustSucceedAllocated = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED);
|
||
|
||
if (RxContext == RxDeviceObject->StartStopContext.pStopContext) {
|
||
RxDeviceObject->StartStopContext.pStopContext = NULL;
|
||
} else {
|
||
ASSERT((RxContext->ContextListEntry.Flink->Blink == &RxContext->ContextListEntry) &&
|
||
(RxContext->ContextListEntry.Blink->Flink == &RxContext->ContextListEntry));
|
||
|
||
RemoveEntryList(&RxContext->ContextListEntry);
|
||
|
||
if ((InterlockedDecrement(&RxDeviceObject->NumberOfActiveContexts)==0) &&
|
||
(RxDeviceObject->StartStopContext.pStopContext != NULL)) {
|
||
pStopContext = RxDeviceObject->StartStopContext.pStopContext;
|
||
}
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql );
|
||
|
||
if (FinalRefCount > 0) {
|
||
RxDbgTraceLV(-1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext, RxContext not final!!! = %08lx (%lu)\n",
|
||
RxContext,RxContext->SerialNumber));
|
||
return;
|
||
}
|
||
|
||
ASSERT(RxContext->ReferenceCount == 0);
|
||
|
||
// Clean up the operation specific stuff
|
||
RxPrepareContextForReuse(RxContext);
|
||
|
||
ASSERT(RxContext->AcquireReleaseFcbTrackerX == 0);
|
||
|
||
if (pStopContext != NULL) {
|
||
// Signal the event.
|
||
RxSignalSynchronousWaiter(pStopContext);
|
||
}
|
||
|
||
if (RxContext->dwShadowCritOwner)
|
||
{
|
||
DbgPrint("RxDereferenceAndDeleteRxContext:shdowcrit still owned by %x\n", RxContext->dwShadowCritOwner);
|
||
ASSERT(FALSE);
|
||
}
|
||
if (RxContextIsFromPool) {
|
||
ExFreeToNPagedLookasideList(
|
||
&RxContextLookasideList,
|
||
RxContext );
|
||
}
|
||
|
||
RxDbgTraceLV(-1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext -> VOID\n", 0));
|
||
|
||
return;
|
||
}
|
||
|
||
ULONG RxStopOnLoudCompletion = TRUE;
|
||
NTSTATUS
|
||
RxCompleteRequest(
|
||
IN PRX_CONTEXT RxContext,
|
||
IN NTSTATUS Status)
|
||
{
|
||
PIRP Irp;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(RxContext);
|
||
ASSERT(RxContext->CurrentIrp);
|
||
|
||
Irp = RxContext->CurrentIrp;
|
||
if ((RxContext->LoudCompletionString)) {
|
||
DbgPrint("LoudCompletion %08lx/%08lx on %wZ\n",Status,Irp->IoStatus.Information,RxContext->LoudCompletionString);
|
||
if ((Status!=STATUS_SUCCESS) && RxStopOnLoudCompletion) {
|
||
DbgPrint("FAILURE!!!!! %08lx/%08lx on %wZ\n",Status,Irp->IoStatus.Information,RxContext->LoudCompletionString);
|
||
//DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
if ((Status!=STATUS_SUCCESS) && (RxContext->LoudCompletionString)) {
|
||
DbgPrint("LoudFailure %08lx/%08lx on %wZ\n",Status,Irp->IoStatus.Information,RxContext->LoudCompletionString);
|
||
if (RxStopOnLoudCompletion) {
|
||
//DbgBreakPoint();
|
||
}
|
||
}
|
||
#endif
|
||
|
||
RxContext->CurrentIrp = NULL;
|
||
RxCompleteRequest_Real(RxContext,Irp,Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
#ifdef RDBSSLOG
|
||
//this stuff must be in nonpaged memory
|
||
//// 1 2 3 4 5 6 7 8 9
|
||
char RxCompleteContext_SurrogateFormat[] = "%S%S%S%N%N%N%N%N%N";
|
||
//// 2 3 4 5 6 7 8 9
|
||
char RxCompleteContext_ActualFormat[] = "Irp-- %s%s/%lx %lx irp %lx iosb %lx,%lx #%lx";
|
||
|
||
#endif //ifdef RDBSSLOG
|
||
|
||
|
||
VOID
|
||
RxCompleteRequest_Real (
|
||
IN PRX_CONTEXT RxContext,
|
||
IN PIRP Irp OPTIONAL,
|
||
IN NTSTATUS Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine completes a Irp
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Status - Supplies the status to complete the Irp with
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// If we have an Irp then complete the irp.
|
||
//
|
||
|
||
if (Irp != NULL) {
|
||
|
||
CCHAR PriorityBoost;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
RxSetCancelRoutine(Irp,NULL);
|
||
|
||
//
|
||
// For an error, zero out the information field before
|
||
// completing the request if this was an input operation.
|
||
// Otherwise IopCompleteRequest will try to copy to the user's buffer.
|
||
// Also, no boost for an error.
|
||
//
|
||
|
||
if ( NT_ERROR(Status) &&
|
||
FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
PriorityBoost = IO_NO_INCREMENT;
|
||
} else {
|
||
PriorityBoost = IO_DISK_INCREMENT;
|
||
}
|
||
|
||
if (Irp->MdlAddress) {
|
||
RxUnprotectMdlFromFree(Irp->MdlAddress);
|
||
}
|
||
|
||
Irp->IoStatus.Status = Status;
|
||
|
||
RxDbgTrace(0, (DEBUG_TRACE_DISPATCH),
|
||
("RxCompleteRequest_real ---------- Irp(code) = %08lx(%02lx) %08lx %08lx\n",
|
||
Irp, IoGetCurrentIrpStackLocation( Irp )->MajorFunction,
|
||
Status, Irp->IoStatus.Information));
|
||
|
||
if (RxContext != NULL) {
|
||
ASSERT(RxContext->MajorFunction<=IRP_MJ_MAXIMUM_FUNCTION);
|
||
|
||
if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
|
||
RxLog(
|
||
(
|
||
RxCompleteContext_SurrogateFormat,
|
||
RxCompleteContext_ActualFormat,
|
||
(RxContext->OriginalThread == PsGetCurrentThread())?"":"*",
|
||
RXCONTX_OPERATION_NAME(RxContext->MajorFunction,TRUE),
|
||
RxContext->MinorFunction,
|
||
RxContext,
|
||
Irp,
|
||
Status,
|
||
Irp->IoStatus.Information,
|
||
RxContext->SerialNumber
|
||
));
|
||
|
||
RxWmiLog(LOG,
|
||
RxCompleteRequest,
|
||
LOGPTR(RxContext)
|
||
LOGPTR(Irp)
|
||
LOGULONG(Status)
|
||
LOGPTR(Irp->IoStatus.Information)
|
||
LOGULONG(RxContext->SerialNumber)
|
||
LOGUCHAR(RxContext->MinorFunction)
|
||
LOGARSTR(RXCONTX_OPERATION_NAME(RxContext->MajorFunction,TRUE)));
|
||
}
|
||
}
|
||
|
||
if ((IrpSp->MajorFunction == IRP_MJ_CREATE) &&
|
||
(Status != STATUS_PENDING)) {
|
||
if (RxContext != NULL) {
|
||
if (FlagOn(
|
||
RxContext->Create.Flags,
|
||
RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH)) {
|
||
IrpSp->FileObject->FileName.Length += sizeof(WCHAR);
|
||
}
|
||
|
||
RxpPrepareCreateContextForReuse(RxContext);
|
||
|
||
ASSERT ( RxContext->Create.CanonicalNameBuffer == NULL );
|
||
}
|
||
}
|
||
|
||
if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
|
||
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
||
ASSERT(Irp->IoStatus.Information <= IrpSp->Parameters.Write.Length);
|
||
}
|
||
}
|
||
|
||
if (RxContext != NULL) {
|
||
if (RxContext->PendingReturned) {
|
||
ASSERT(IrpSp->Control & SL_PENDING_RETURNED);
|
||
}
|
||
}
|
||
|
||
IoCompleteRequest( Irp, PriorityBoost );
|
||
} else {
|
||
// a call with a null irp..........
|
||
RxLog(("Irp00 %lx\n", RxContext ));
|
||
RxWmiLog(LOG,
|
||
RxCompleteRequest_NI,
|
||
LOGPTR(RxContext));
|
||
}
|
||
|
||
// Delete the Irp context.
|
||
if (RxContext != NULL) {
|
||
RxDereferenceAndDeleteRxContext( RxContext );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
__RxSynchronizeBlockingOperationsMaybeDroppingFcbLock(
|
||
IN OUT PRX_CONTEXT RxContext,
|
||
IN OUT PLIST_ENTRY BlockingIoQ,
|
||
IN BOOLEAN DropFcbLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to synchronize among blocking IOs to the same Q.
|
||
Currently, the routine is only used to synchronize block pipe operations and
|
||
the Q is the one in the file object extension (Fobx). What happens is that
|
||
the operation joins the queue. If it is now the front of the queue, the
|
||
operation continues; otherwise it waits on the sync event in the RxContext
|
||
or just returns pending (if async).
|
||
|
||
We may have been cancelled while we slept, check for that and
|
||
return an error if it happens.
|
||
|
||
The event must have been reset before the call. The fcb lock must be held;
|
||
it is dropped after we get on the Q.
|
||
|
||
Arguments:
|
||
|
||
RxContext The context of the operation being synchronized
|
||
|
||
BlockingIoQ The queue to get on.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
RxCaptureFcb;
|
||
RxCaptureRequestPacket;
|
||
|
||
BOOLEAN FcbLockDropped = FALSE;
|
||
BOOLEAN SerializationMutexReleased = FALSE;
|
||
|
||
PRX_CONTEXT FrontRxContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace(+1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock, rxc=%08lx, fobx=%08lx\n", RxContext, RxContext->pFobx ));
|
||
|
||
RxContext->StoredStatus = STATUS_SUCCESS; //do this early....a cleanup could come thru and change it
|
||
|
||
ExAcquireFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
if (!FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_CANCELLED)) {
|
||
|
||
SetFlag(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
||
|
||
// ValidateBlockingIoQ(BlockingIoQ);
|
||
|
||
InsertTailList(BlockingIoQ,&RxContext->RxContextSerializationQLinks);
|
||
FrontRxContext = CONTAINING_RECORD( BlockingIoQ->Flink,RX_CONTEXT,RxContextSerializationQLinks);
|
||
|
||
if (RxContext != FrontRxContext) {
|
||
if (!FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
||
if (!SerializationMutexReleased) {
|
||
SerializationMutexReleased = TRUE;
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
}
|
||
|
||
if (DropFcbLock && !FcbLockDropped) {
|
||
RxContext->FcbResourceAcquired = FALSE;
|
||
FcbLockDropped = TRUE;
|
||
RxReleaseFcb(RxContext,capFcb);
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock waiting, rxc=%08lx\n", RxContext ));
|
||
|
||
RxWaitSync(RxContext);
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock ubblocked, rxc=%08lx\n", RxContext ));
|
||
} else {
|
||
RxContext->StoredStatus = STATUS_PENDING;
|
||
SetFlag(RxContext->Flags,RX_CONTEXT_FLAG_BLOCKED_PIPE_RESUME);
|
||
|
||
try {
|
||
RxPrePostIrp(RxContext,capReqPacket);
|
||
} finally {
|
||
if (AbnormalTermination()) {
|
||
RxLog(("!!!!! RxContext %lx Status %lx\n",RxContext, RxContext->StoredStatus));
|
||
RxWmiLog(LOG,
|
||
RxSynchronizeBlockingOperationsMaybeDroppingFcbLock,
|
||
LOGPTR(RxContext)
|
||
LOGULONG(Status));
|
||
RemoveEntryList(&RxContext->RxContextSerializationQLinks);
|
||
|
||
RxContext->RxContextSerializationQLinks.Flink = NULL;
|
||
RxContext->RxContextSerializationQLinks.Blink = NULL;
|
||
|
||
ClearFlag(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
||
|
||
if (!SerializationMutexReleased) {
|
||
SerializationMutexReleased = TRUE;
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
}
|
||
} else {
|
||
InterlockedIncrement(&RxContext->ReferenceCount);
|
||
}
|
||
}
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock asyncreturn, rxc=%08lx\n", RxContext ));
|
||
}
|
||
}
|
||
|
||
Status = (FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_CANCELLED) ?
|
||
STATUS_CANCELLED :
|
||
RxContext->StoredStatus);
|
||
|
||
} else {
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
if (!SerializationMutexReleased) {
|
||
SerializationMutexReleased = TRUE;
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
}
|
||
|
||
if (DropFcbLock && !FcbLockDropped) {
|
||
RxContext->FcbResourceAcquired = FALSE;
|
||
FcbLockDropped = TRUE;
|
||
RxReleaseFcb(RxContext,capFcb);
|
||
}
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock returning, rxc=%08lx, status=%08lx\n", RxContext, Status ));
|
||
|
||
return(Status);
|
||
}
|
||
|
||
VOID
|
||
RxRemoveOperationFromBlockingQueue(
|
||
IN OUT PRX_CONTEXT RxContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes the context from the blocking queue if it is on it
|
||
|
||
Arguments:
|
||
|
||
RxContext The context of the operation being synchronized
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExAcquireFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
if (FlagOn(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) {
|
||
ClearFlag(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
||
|
||
RemoveEntryList(&RxContext->RxContextSerializationQLinks);
|
||
|
||
RxContext->RxContextSerializationQLinks.Flink = NULL;
|
||
RxContext->RxContextSerializationQLinks.Blink = NULL;
|
||
}
|
||
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxRemoveOperationFromBlockingQueue, rxc=%08lx\n", RxContext ));
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RxCancelBlockingOperation(
|
||
IN OUT PRX_CONTEXT RxContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cancels the operation in the blocking queue
|
||
|
||
Arguments:
|
||
|
||
RxContext The context of the operation being synchronized
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExAcquireFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
if (FlagOn(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) {
|
||
ClearFlag(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
||
|
||
RemoveEntryList(&RxContext->RxContextSerializationQLinks);
|
||
|
||
RxContext->RxContextSerializationQLinks.Flink = NULL;
|
||
RxContext->RxContextSerializationQLinks.Blink = NULL;
|
||
|
||
if (!FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
||
RxSignalSynchronousWaiter(RxContext);
|
||
} else {
|
||
// The reference taken in the synchronization routine is derefernced
|
||
// by the post completion routine,
|
||
RxFsdPostRequest(RxContext);
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxCancelBlockedOperations, rxc=%08lx\n", RxContext ));
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RxResumeBlockedOperations_Serially(
|
||
IN OUT PRX_CONTEXT RxContext,
|
||
IN OUT PLIST_ENTRY BlockingIoQ
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine wakes up the next guy, if any, on the serialized blockingioQ. We know that the fcb must still be valid because
|
||
of the reference that is being held by the IO system on the file object thereby preventing a close.
|
||
|
||
Arguments:
|
||
|
||
RxContext The context of the operation being synchronized
|
||
BlockingIoQ The queue to get on.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
BOOLEAN FcbLockHeld = FALSE;
|
||
PRX_CONTEXT FrontRxContext = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace(
|
||
+1,
|
||
Dbg,
|
||
("RxResumeBlockedOperations_Serially, rxc=%08lx, fobx=%08lx\n",
|
||
RxContext,
|
||
RxContext->pFobx ));
|
||
|
||
//remove myself from the queue and check for someone else
|
||
ExAcquireFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
if (FlagOn(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) {
|
||
ClearFlag(
|
||
RxContext->FlagsForLowIo,
|
||
RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
||
|
||
// ValidateBlockingIoQ(BlockingIoQ);
|
||
RemoveEntryList(&RxContext->RxContextSerializationQLinks);
|
||
// ValidateBlockingIoQ(BlockingIoQ);
|
||
|
||
RxContext->RxContextSerializationQLinks.Flink = NULL;
|
||
RxContext->RxContextSerializationQLinks.Blink = NULL;
|
||
|
||
ListEntry = BlockingIoQ->Flink;
|
||
|
||
if (BlockingIoQ != ListEntry) {
|
||
FrontRxContext = CONTAINING_RECORD(
|
||
ListEntry,
|
||
RX_CONTEXT,
|
||
RxContextSerializationQLinks);
|
||
RxDbgTrace(
|
||
-1,
|
||
Dbg,
|
||
("RxResumeBlockedOperations unwaiting the next guy and returning, rxc=%08lx\n",
|
||
RxContext ));
|
||
} else {
|
||
FrontRxContext = NULL;
|
||
}
|
||
|
||
if (FrontRxContext != NULL) {
|
||
if (!FlagOn(FrontRxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
||
RxSignalSynchronousWaiter(FrontRxContext);
|
||
} else {
|
||
// The reference taken in the synchronization routine is derefernced
|
||
// by the post completion routine,
|
||
RxFsdPostRequest(FrontRxContext);
|
||
}
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutex(&RxContextPerFileSerializationMutex);
|
||
|
||
RxDbgTrace(-1, Dbg, ("RxResumeBlockedOperations_Serially returning, rxc=%08lx\n", RxContext ));
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RxResumeBlockedOperations_ALL(
|
||
IN OUT PRX_CONTEXT RxContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine wakes up all of the guys on the blocked operations queue. The controlling mutex is also
|
||
stored in the RxContext block. the current implementation is that all of the guys must be waiting
|
||
on the sync events.
|
||
|
||
Arguments:
|
||
|
||
RxContext The context of the operation being synchronized
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY CopyOfQueue;
|
||
PLIST_ENTRY ListEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace(+1, Dbg, ("RxResumeBlockedOperations_ALL, rxc=%08lx\n", RxContext ));
|
||
|
||
RxTransferListWithMutex(
|
||
&CopyOfQueue,
|
||
&RxContext->BlockedOperations,
|
||
RxContext->BlockedOpsMutex);
|
||
|
||
for (ListEntry = CopyOfQueue.Flink;
|
||
ListEntry != &CopyOfQueue;
|
||
){
|
||
PRX_CONTEXT FrontRxContext = CONTAINING_RECORD(
|
||
ListEntry,
|
||
RX_CONTEXT,
|
||
RxContextSerializationQLinks);
|
||
|
||
RxSignalSynchronousWaiter(FrontRxContext);
|
||
|
||
IF_DEBUG {
|
||
PLIST_ENTRY PrevListEntry = ListEntry;
|
||
ListEntry = ListEntry->Flink;
|
||
PrevListEntry->Flink = PrevListEntry->Blink = NULL;
|
||
} else {
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
}
|
||
|
||
RxDbgTrace(
|
||
-1,
|
||
Dbg,
|
||
("RxResumeBlockedOperations_ALL returning, rxc=%08lx\n",
|
||
RxContext ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
__RxItsTheSameContext(
|
||
PRX_CONTEXT RxContext,
|
||
ULONG CapturedRxContextSerialNumber,
|
||
ULONG Line,
|
||
PSZ File
|
||
)
|
||
{
|
||
if ((NodeType(RxContext)!=RDBSS_NTC_RX_CONTEXT) ||
|
||
(RxContext->SerialNumber != CapturedRxContextSerialNumber)) {
|
||
RxLog(("NotSame!!!! %lx",RxContext));
|
||
RxWmiLog(LOG,
|
||
RxItsTheSameContext,
|
||
LOGPTR(RxContext));
|
||
|
||
DbgPrint("NOT THE SAME CONTEXT %08lx at Line %d in %s\n",
|
||
RxContext,Line,File);
|
||
//DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
VOID
|
||
ValidateBlockingIoQ(
|
||
PLIST_ENTRY BlockingIoQ
|
||
)
|
||
{
|
||
PLIST_ENTRY pListEntry;
|
||
ULONG cntFlink, cntBlink;
|
||
|
||
cntFlink = cntBlink = 0;
|
||
|
||
pListEntry = BlockingIoQ->Flink;
|
||
|
||
while (pListEntry != BlockingIoQ) {
|
||
PRX_CONTEXT pRxContext;
|
||
|
||
pRxContext = (PRX_CONTEXT)CONTAINING_RECORD(
|
||
pListEntry,
|
||
RX_CONTEXT,
|
||
RxContextSerializationQLinks);
|
||
|
||
|
||
if (!pRxContext || (NodeType(pRxContext) != RDBSS_NTC_RX_CONTEXT))
|
||
{
|
||
|
||
DbgPrint("ValidateBlockingIO:Invalid RxContext %x on Q %x\n",
|
||
pRxContext, BlockingIoQ);
|
||
//DbgBreakPoint();
|
||
}
|
||
|
||
|
||
++cntFlink;
|
||
pListEntry = pListEntry->Flink;
|
||
}
|
||
|
||
// check backward list validity
|
||
|
||
pListEntry = BlockingIoQ->Blink;
|
||
|
||
while (pListEntry != BlockingIoQ) {
|
||
PRX_CONTEXT pRxContext;
|
||
|
||
pRxContext = (PRX_CONTEXT)CONTAINING_RECORD(
|
||
pListEntry,
|
||
RX_CONTEXT,
|
||
RxContextSerializationQLinks);
|
||
|
||
|
||
if (!pRxContext || (NodeType(pRxContext) != RDBSS_NTC_RX_CONTEXT))
|
||
{
|
||
|
||
DbgPrint("ValidateBlockingIO:Invalid RxContext %x on Q %x\n",
|
||
pRxContext, BlockingIoQ);
|
||
//DbgBreakPoint();
|
||
}
|
||
|
||
++cntBlink;
|
||
pListEntry = pListEntry->Blink;
|
||
}
|
||
|
||
// both counts should be the same
|
||
if(cntFlink != cntBlink)
|
||
{
|
||
DbgPrint("ValidateBlockingIO: cntFlink %d cntBlink %d\n", cntFlink, cntBlink);
|
||
//DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
#ifndef RX_NO_DBGFIELD_HLPRS
|
||
|
||
#define DECLARE_FIELD_HLPR(x) ULONG RxContextField_##x = FIELD_OFFSET(RX_CONTEXT,x);
|
||
#define DECLARE_FIELD_HLPR2(x,y) ULONG RxContextField_##x##y = FIELD_OFFSET(RX_CONTEXT,x.y);
|
||
|
||
DECLARE_FIELD_HLPR(MajorFunction);
|
||
DECLARE_FIELD_HLPR(CurrentIrp);
|
||
DECLARE_FIELD_HLPR(pFcb);
|
||
DECLARE_FIELD_HLPR(Flags);
|
||
DECLARE_FIELD_HLPR(MRxContext);
|
||
DECLARE_FIELD_HLPR(MRxCancelRoutine);
|
||
DECLARE_FIELD_HLPR(SyncEvent);
|
||
DECLARE_FIELD_HLPR(BlockedOperations);
|
||
DECLARE_FIELD_HLPR(FlagsForLowIo);
|
||
DECLARE_FIELD_HLPR2(Create,CanonicalNameBuffer);
|
||
DECLARE_FIELD_HLPR2(Create,pSrvCall);
|
||
DECLARE_FIELD_HLPR2(Create,pNetRoot);
|
||
DECLARE_FIELD_HLPR2(Create,pVNetRoot);
|
||
DECLARE_FIELD_HLPR2(QueryDirectory,FileIndex);
|
||
DECLARE_FIELD_HLPR2(QueryEa,UserEaList);
|
||
DECLARE_FIELD_HLPR2(QuerySecurity,SecurityInformation);
|
||
DECLARE_FIELD_HLPR2(QuerySecurity,Length);
|
||
#endif
|
||
|
||
|