Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

768 lines
24 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
InnerIo.c
Abstract:
This module implements the read, write, and lockctrl routines for the proxy minirdr
Author:
Joe Linn [JoeLinn] 11-Oct-1994
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_READ)
#ifdef ALLOC_PRAGMA
#endif
NTSTATUS
MRxProxyBuildAsynchronousRequest(
IN PRX_CONTEXT RxContext,
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL
)
/*++
Routine Description:
This routine builds an I/O Request Packet (IRP) suitable for a File System
Driver (FSD) to use in requesting an I/O operation from a device driver.
The request (RxContext->MajorFunction) must be one of the following request codes:
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_DIRECTORY_CONTROL
IRP_MJ_FLUSH_BUFFERS
//IRP_MJ_SHUTDOWN (not yet implemented)
Arguments:
CompletionRoutine - the IrpCompletionRoutine
Return Value:
The function value is a pointer to the IRP representing the specified
request.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
ULONG MajorFunction = RxContext->MajorFunction;
RxCaptureFcb; RxCaptureFobx;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PMRXPROXY_RX_CONTEXT pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext);
PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext =
(PMRXPROXY_ASYNCENGINE_CONTEXT)(pMRxProxyContext->AsyncEngineContext);
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
PMRX_PROXY_SRV_OPEN proxySrvOpen = MRxProxyGetSrvOpenExtension(capFobx->pSrvOpen);
//PMRX_PROXY_FCB proxyFcb = MRxProxyGetFcbExtension(capFcb);
PDEVICE_OBJECT DeviceObject = proxySrvOpen->UnderlyingDeviceObject;
PFILE_OBJECT FileObject = proxySrvOpen->UnderlyingFileObject;
LARGE_INTEGER ZeroAsLI;
ULONG MdlLength = 0;
PAGED_CODE();
ASSERT (proxySrvOpen->UnderlyingFileObject);
if (DeviceObject->Flags & DO_BUFFERED_IO) {
//i cannot handled buffered_io devices....sigh
return STATUS_INVALID_DEVICE_REQUEST;
}
RxDbgTrace(0, Dbg, ("MRxProxyBuildAsynchronousRequest %08lx %08lx len/off=%08lx %08lx\n",
RxContext,SrvOpen,
LowIoContext->ParamsFor.ReadWrite.ByteCount,
(ULONG)LowIoContext->ParamsFor.ReadWrite.ByteOffset));
RxLog(("BuildAsyncIrp %lx %lx %lx %lx",
RxContext,SrvOpen,
LowIoContext->ParamsFor.ReadWrite.ByteCount,
(ULONG)LowIoContext->ParamsFor.ReadWrite.ByteOffset));
ZeroAsLI.QuadPart = 0;
// irp = IoBuildAsynchronousFsdRequest(
// MajorFunction,
// DeviceObject,
// NULL,
// 0,
// &ZeroAsLI,
// NULL
// );
//
// if (!irp) {
// return STATUS_INSUFFICIENT_RESOURCES;
// }
irp = IoAllocateIrp( DeviceObject->StackSize, FALSE ); //why not charge???
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Set current thread for IoSetHardErrorOrVerifyDevice.
//
irp->Tail.Overlay.Thread = PsGetCurrentThread();
//
// Get a pointer to the stack location of the first driver which will be
// invoked. This is where the function codes and the parameters are set.
//
irpSp = IoGetNextIrpStackLocation( irp ); //ok4ioget
irpSp->MajorFunction = (UCHAR) MajorFunction;
irpSp->FileObject = FileObject; //ok4->FileObj
RxLog(("BuildAsyncIrpFo %lx %lx",RxContext,FileObject));
{ BOOLEAN EnableCalls = CompletionRoutine!=NULL;
IoSetCompletionRoutine(irp, CompletionRoutine, RxContext,
EnableCalls,EnableCalls,EnableCalls);
}
if ( (MajorFunction == IRP_MJ_READ) || (MajorFunction == IRP_MJ_WRITE) ) {
// never paging io
BOOLEAN PagingIo = FALSE;
//BOOLEAN PagingIo =
// BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO);
irp->Flags |= IRP_NOCACHE;
//
// Set the parameters according to whether this is a read or a write
// operation. Notice that these parameters must be set even if the
// driver has not specified buffered or direct I/O.
//
ASSERT (&irpSp->Parameters.Write.Key == &irpSp->Parameters.Read.Key);
ASSERT (&irpSp->Parameters.Write.Length == &irpSp->Parameters.Read.Length);
ASSERT (&irpSp->Parameters.Write.ByteOffset == &irpSp->Parameters.Read.ByteOffset);
irpSp->Parameters.Read.Key = LowIoContext->ParamsFor.ReadWrite.Key;
irpSp->Parameters.Read.ByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
irp->RequestorMode = KernelMode;
irp->UserBuffer = RxLowIoGetBufferAddress(RxContext);
MdlLength = RxContext->CurrentIrp->MdlAddress->ByteCount;
if (PagingIo) {
irpSp->Parameters.Read.Length = MdlLength;
} else {
irpSp->Parameters.Read.Length = LowIoContext->ParamsFor.ReadWrite.ByteCount;
}
} else if (MajorFunction == IRP_MJ_FLUSH_BUFFERS) {
MdlLength = 0;
//nothing else to do!!!
} else {
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
PVOID Buffer = RxContext->Info.Buffer;
PULONG pLengthRemaining = &RxContext->Info.LengthRemaining;
BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
ASSERT( MajorFunction == IRP_MJ_DIRECTORY_CONTROL );
irpSp->MinorFunction = IRP_MN_QUERY_DIRECTORY;
//CODE.IMPROVEMENT it would be better to actually get the stuff out of the context.info
irpSp->Parameters.QueryDirectory = RxContext->CurrentIrpSp->Parameters.QueryDirectory;
ASSERT (
(irpSp->Parameters.QueryDirectory.FileInformationClass == FileInformationClass)
&& (irpSp->Parameters.QueryDirectory.Length == *pLengthRemaining)
);
irpSp->Flags = RxContext->CurrentIrpSp->Flags;
irp->UserBuffer = Buffer;
MdlLength = *pLengthRemaining;
if (Wait) {
irp->Flags |= IRP_SYNCHRONOUS_API;
}
}
// Build an mdl if necessary....
if (MdlLength != 0) {
irp->MdlAddress = IoAllocateMdl(irp->UserBuffer,MdlLength,
FALSE,FALSE,NULL);
if (!irp->MdlAddress) {
//whoops.......sorry..........
IoFreeIrp(irp);
return(STATUS_INSUFFICIENT_RESOURCES);
}
MmBuildMdlForNonPagedPool(irp->MdlAddress);
}
//
// Finally, return a pointer to the IRP.
//
AsyncEngineContext->CalldownIrp = irp;
RxLog(("BAsyIrpX %lx %lx %lx %lx %lx %lx %lx",
RxContext,
irpSp->MajorFunction,
irpSp->Flags,
irpSp->Parameters.Others.Argument1,
irpSp->Parameters.Others.Argument2,
irpSp->Parameters.Others.Argument3,
irpSp->Parameters.Others.Argument4));
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//
// The local debug trace level
//
#undef Dbg
#define Dbg (DEBUG_TRACE_WRITE)
//everything else in here is ifdef'd out....also CODE.IMPROVEMENT we should change over
//to a common irp building routine in Rx. the code is already in the csc driver
#if 0
NTSTATUS
MRxProxyBuildInnerIoAsyncWrite (
IN PRX_CONTEXT RxContext,
IN PBYTE Buffer,
IN ULONG WriteByteCount,
IN PLARGE_INTEGER FileOffset,
IN OUT PIRP *CalldownIrp
)
/*++
Routine Description:
This routine fills in the calldown irp for a proxy read
Arguments:
RxContext,
PBYTE Buffer - the write buffer
ULONG Count - amount of data to written
PLARGE_INTEGER FileOffset - fileoffset where the write begins
PIRP *CallDownIrp - where the Irp is to be stored
Return Value:
NTSTATUS - Returns the status for the write
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb; RxCaptureFobx;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
PMRX_PROXY_SRV_OPEN proxySrvOpen = MRxProxyGetSrvOpenExtension(SrvOpen);
RxDbgTrace(+1, Dbg, ("MRxProxyInnerIoWrite.... ByteCount = %08lx, ByteOffset = %08lx %08lx\n",
LowIoContext->ParamsFor.ReadWrite.ByteCount,
FileOffset->LowPart, FileOffset->HighPart));
RxLog(("MRxProxyInnerIoWrite %lx %lx %lx",
SrvOpen,
LowIoContext->ParamsFor.ReadWrite.ByteCount,
FileOffset->LowPart));
RxDbgTrace ( 0, Dbg, ( "MRxProxyInnerIoWrite.... ->Buffer = %8lx\n", Buffer));
*CalldownIrp = MRxProxyBuildAsynchronousReadWriteRequest(
RxContext, // IN PVOID Context
MRxProxyAsyncEngineCalldownIrpCompletion // IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
);
if (!*CalldownIrp){
Status = (STATUS_INSUFFICIENT_RESOURCES);
}
RxDbgTrace(-1, Dbg, ("MRxProxyInnerIoWrite.... exit\n"));
return(Status);
}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//
// The local debug trace level
//
#undef Dbg
#define Dbg (DEBUG_TRACE_LOCKCTRL)
PIRP
MRxBuildLockRequest (
IN PIRP irp OPTIONAL,
IN PFILE_OBJECT fileObject,
IN PETHREAD UsersThread,
IN UCHAR MinorCode,
IN RXVBO ByteOffset,
IN PLARGE_INTEGER Length,
IN ULONG Key,
IN UCHAR Flags,
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
IN PVOID Context OPTIONAL
)
/*++
Routine Description:
This function builds an I/O request packet for a lock request.
Arguments:
Irp - Supplies a pointer to an IRP; allocates one if one is not provided.
FileObject - Supplies a pointer the file object to which this
request is directed. This pointer is copied into the IRP, so
that the called driver can find its file-based context. NOTE
THAT THIS IS NOT A REFERENCED POINTER. The caller must ensure
that the file object is not deleted while the I/O operation is
in progress. The proxy minirdr accomplishes this by holding a pointer
(a REFERENCED ptr) to the fileobject in its srvopen while the fileobject is
open.
Context - Supplies a PVOID value that is passed to the completion
routine.
StartingBlock - the block number of the beginning of the locked
range.
ByteOffset - the offset within block of the beginning of the locked
range.
Length - the length of the locked range.
Key - the key value to be associated with the lock.
Return Value:
None.
--*/
{
PDEVICE_OBJECT deviceObject;
PIO_STACK_LOCATION irpSp;
PAGED_CODE( );
deviceObject = IoGetRelatedDeviceObject( fileObject );
//
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
// The allocation is performed with an exception handler in case the
// caller does not have enough quota to allocate the packet.
if (irp) {
ASSERT( irp->StackCount >= deviceObject->StackSize );
} else {
irp = IoAllocateIrp( deviceObject->StackSize, TRUE ); //joejoe should i charge quota??
}
if (!irp) {
//
// An IRP could not be allocated.
return NULL;
}
// we have to make sure that the thread that takes the lock is the same as the one that reads
irp->Tail.Overlay.Thread = UsersThread;
irp->RequestorMode = KernelMode;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation( irp );
{ BOOLEAN EnableCalls = CompletionRoutine!=NULL;
IoSetCompletionRoutine(irp, CompletionRoutine, Context,
EnableCalls,EnableCalls,EnableCalls);
}
irpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
irpSp->MinorFunction = MinorCode;
irpSp->FileObject = fileObject; //ok4->FileObj
irpSp->DeviceObject = deviceObject;
irpSp->Flags = Flags;
irpSp->Parameters.LockControl.Length = Length;
irpSp->Parameters.LockControl.Key = Key;
irpSp->Parameters.LockControl.ByteOffset.QuadPart = ByteOffset;
return irp;
}
NTSTATUS
MRxProxyCalldownLockCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP CalldownIrp,
IN PVOID Context
)
/*++
Routine Description:
This is the I/O completion routine for calldown lock requests. we just call the lowio completion
and exit
Arguments:
DeviceObject - Pointer to target device object for the request.
CalldownIrp - Pointer to I/O request packet used to call down to the underlying filesystem
Context - Irpcontext of the original request to the rdbss
Return Value:
NTSTATUS - (STATUS_MORE_PROCESSING_REQUIRED) is returned.
--*/
{
PRX_CONTEXT RxContext = Context;
RxDbgTrace ( 0, Dbg, ("MRxProxyCalldownLockCompletion = %08lx\n", 0));
//DbgBreakPoint();
if (CalldownIrp->PendingReturned){
RxContext->CurrentIrp->IoStatus = CalldownIrp->IoStatus;
RxContext->StoredStatus = CalldownIrp->IoStatus.Status;
}
RxLowIoCompletion(RxContext);
IoFreeIrp(CalldownIrp);
return((STATUS_MORE_PROCESSING_REQUIRED));
}
NTSTATUS
MRxProxyLocks (
IN PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine implements proxy read call. we fill in the info and stuff here BUT we
do not complete the Irp.
Arguments:
Return Value:
NTSTATUS - Returns the status for the read
--*/
{
NTSTATUS Status;
RxCaptureRequestPacket;
PIRP CalldownIrp;
RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PSRV_OPEN SrvOpen = capFobx->SrvOpen;
PMRX_LOCAL_SRV_OPEN proxySrvOpen = (PMRX_LOCAL_SRV_OPEN)SrvOpen;
BOOLEAN Wait = FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WAIT)!=0;
PLARGE_INTEGER LengthAsLI = (PLARGE_INTEGER)&LowIoContext->ParamsFor.Locks.Length;
PLARGE_INTEGER OffsetAsLI = (PLARGE_INTEGER)&LowIoContext->ParamsFor.Locks.ByteOffset;
char *whichop;
switch (LowIoContext->Operation) {
case LOWIO_OP_SHAREDLOCK: whichop = "SHAREDLOCK"; break;
case LOWIO_OP_EXCLUSIVELOCK: whichop = "EXCLUSIVELOCK"; break;
case LOWIO_OP_UNLOCK: whichop = "UNLOCK"; break;
case LOWIO_OP_UNLOCKALL: whichop = "UNLOCKALL"; break;
case LOWIO_OP_UNLOCKALLBYKEY: whichop = "UNLOCKALLBYKEY"; break;
}
RxDbgTrace (+1, Dbg, ("MRxExclusiveLock...%s, Flags = %08lx, Key = %08lx\n", whichop,
LowIoContext->ParamsFor.Locks.Flags,
LowIoContext->ParamsFor.Locks.Key));
RxDbgTrace( 0, Dbg, (" ->Length = %08lx %08lx\n",
LengthAsLI->LowPart,
LengthAsLI->HighPart));
RxDbgTrace( 0, Dbg, (" ->ByteOffset = %08lx %08lx\n",
OffsetAsLI->LowPart,
OffsetAsLI->HighPart));
RxLog(('kLz',3,SrvOpen, LengthAsLI->LowPart, OffsetAsLI->LowPart));
ASSERT (proxySrvOpen->UnderlyingFileObject);
CalldownIrp = MRxBuildLockRequest (
NULL ,//IN PIRP irp OPTIONAL,
proxySrvOpen->UnderlyingFileObject ,//IN PFILE_OBJECT fileObject,
capReqPacket->Tail.Overlay.Thread ,//IN PTHREAD UsersThread,
capPARAMS->MinorFunction ,//IN UCHAR MinorCode,
LowIoContext->ParamsFor.Locks.ByteOffset,//IN RXVBO ByteOffset,
LengthAsLI ,//IN PLARGE_INTEGER Length,
LowIoContext->ParamsFor.Locks.Key ,//IN ULONG Key,
(UCHAR)LowIoContext->ParamsFor.Locks.Flags ,//IN UCHAR Flags,
MRxProxyCalldownLockCompletion ,//IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
RxContext //IN PVOID Context OPTIONAL
);
if (!CalldownIrp){
Status = RxContext->StoredStatus = (STATUS_INSUFFICIENT_RESOURCES);
return(Status);
}
Status = RxContext->StoredStatus =
IoCallDriver(
proxySrvOpen->UnderlyingDeviceObject,
CalldownIrp
);
if (Status != (STATUS_PENDING)) {
//copy up the status
capReqPacket->IoStatus = CalldownIrp->IoStatus;
}
RxDbgTrace ( 0, Dbg, (" ---->Initial Status = %08lx\n", Status));
RxDbgTrace(-1, Dbg, (" ------> Initial Block status/Info = %08lx %08lx\n",
capReqPacket->IoStatus.Status, capReqPacket->IoStatus.Information));
return(Status);
}
NTSTATUS
MRxProxyAssertLockCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP CalldownIrp,
IN PVOID Context
)
/*++
Routine Description:
This is the I/O completion routine for calldown ios like querydirectory. the rub is that we
turned synchronous opens into async opens. what we do here is set an event (in the case of a pended
packet for a sync that we turned async) OR copyup the status;complete;free in the case of a call
that was always async.
Arguments:
DeviceObject - Pointer to target device object for the request.
CalldownIrp - Pointer to I/O request packet used to call down to the underlying filesystem
Context - Irpcontext of the original request to the rdbss
Return Value:
NTSTATUS - If (STATUS_MORE_PROCESSING_REQUIRED) is returned.
--*/
{
PKEVENT Event = Context;
RxDbgTrace ( 0, Dbg, (" MRxProxyAssertLockCompletion = %08lx\n", 0));
if (CalldownIrp->PendingReturned){
KeSetEvent( Event, 0, FALSE );
}
return((STATUS_MORE_PROCESSING_REQUIRED));
}
NTSTATUS
MRxProxyAssertBufferedFileLocks (
IN PSRV_OPEN SrvOpen
)
/*++
Routine Description:
This routine is called to assert all buffered file locks on a srvopen.
Arguments:
SrvOpen - Supplies the file whose locks are to be asserted.
Return Value:
NTSTATUS - Status of operation.
--*/
{
NTSTATUS Status;
PFILE_LOCK_INFO FileLock;
PFCB Fcb = SrvOpen->Fcb;
PIRP CalldownIrp = NULL;
PMRX_LOCAL_SRV_OPEN proxySrvOpen = (PMRX_LOCAL_SRV_OPEN)SrvOpen;
UCHAR Flags;
BOOLEAN Wait = TRUE;
PKEVENT Event;
PAGED_CODE();
RxDbgTrace (+1, Dbg, ("MRxProxyAssertBufferedFileLocks SrvOpen = %08lx", SrvOpen));
ASSERT (proxySrvOpen->UnderlyingFileObject);
Event = RxAllocatePoolWithTag( NonPagedPool, sizeof(KEVENT), 'LAxR' );
if (!CalldownIrp){
Status = (STATUS_INSUFFICIENT_RESOURCES);
return(Status);
}
try {
for (FileLock = FsRtlGetNextFileLock(&Fcb->Specific.Fcb.FileLock, TRUE);
FileLock != NULL;
FileLock = FsRtlGetNextFileLock(&Fcb->Specific.Fcb.FileLock, FALSE)) {
RxDbgTrace (0, Dbg, ("MRxProxyAssertBufferedFileLocks Exclusive = %08lx, Key = %08lx\n",
FileLock->ExclusiveLock,
FileLock->Key));
RxDbgTrace( 0, Dbg, (" ->Length = %08lx %08lx\n",
FileLock->Length.LowPart,
FileLock->Length.HighPart));
RxDbgTrace( 0, Dbg, (" ->ByteOffset = %08lx %08lx\n",
FileLock->StartingByte.LowPart,
FileLock->StartingByte.HighPart));
if (FileLock->ExclusiveLock) {
Flags = SL_EXCLUSIVE_LOCK | SL_FAIL_IMMEDIATELY;
} else {
Flags = SL_FAIL_IMMEDIATELY;
}
//joejoe we should reuse the irp.........
CalldownIrp = MRxBuildLockRequest (
NULL ,//IN PIRP irp OPTIONAL,
proxySrvOpen->UnderlyingFileObject ,//IN PFILE_OBJECT fileObject,
proxySrvOpen->OriginalThread ,//IN PTHREAD UsersThread,
IRP_MN_LOCK ,//IN UCHAR MinorCode,
FileLock->StartingByte.QuadPart ,//IN RXVBO ByteOffset,
&FileLock->Length ,//IN PLARGE_INTEGER Length,
FileLock->Key ,//IN ULONG Key,
Flags ,//IN UCHAR Flags,
MRxProxyAssertLockCompletion ,//IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
&Event //IN PVOID Context OPTIONAL
);
if (!CalldownIrp){
Status = (STATUS_INSUFFICIENT_RESOURCES);
try_return(Status);
}
CalldownIrp->Flags |= IRP_SYNCHRONOUS_API;
KeInitializeEvent( Event, SynchronizationEvent, FALSE );
Status = IoCallDriver(
proxySrvOpen->UnderlyingDeviceObject,
CalldownIrp
);
if (Status == (STATUS_PENDING)) {
KeWaitForSingleObject(Event, UserRequest, KernelMode, FALSE, NULL);
Status = CalldownIrp->IoStatus.Status;
}
RxDbgTrace ( 0, Dbg, (" ---->PerLock Status = %08lx\n", Status));
}
try_exit:
NOTHING;
} finally {
ExFreePool(Event);
if (CalldownIrp) {
IoFreeIrp(CalldownIrp);
}
}
RxDbgTrace (-1, Dbg, ("--------->Final Status = %08lx\n", Status));
return Status;
}
#endif