Alex Ionescu <ionucu@videotron.ca>

- Clean up IO Completion Code
- Properly delete an IO Completion. The IRPs were not freed, resulting in memory leaks

Thomas Weidenmueller <w3seek@reactos.com>
- Add SEH to IO Completion Code

svn path=/trunk/; revision=13977
This commit is contained in:
Thomas Bluemel 2005-03-12 19:58:53 +00:00
parent b21f552e3b
commit 7fd17a9a85

View file

@ -1,5 +1,4 @@
/* $Id$ /*
*
* COPYRIGHT: See COPYING in the top level directory * COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel * PROJECT: ReactOS kernel
* FILE: ntoskrnl/io/iocomp.c * FILE: ntoskrnl/io/iocomp.c
@ -18,66 +17,54 @@
POBJECT_TYPE ExIoCompletionType; POBJECT_TYPE ExIoCompletionType;
NPAGED_LOOKASIDE_LIST IoCompletionPacketLookaside; NPAGED_LOOKASIDE_LIST IoCompletionPacketLookaside;
static GENERIC_MAPPING ExIoCompletionMapping = static GENERIC_MAPPING ExIoCompletionMapping =
{ {
STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE, STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE,
STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE, STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | IO_COMPLETION_QUERY_STATE, STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | IO_COMPLETION_QUERY_STATE,
IO_COMPLETION_ALL_ACCESS IO_COMPLETION_ALL_ACCESS
};
static const INFORMATION_CLASS_INFO ExIoCompletionInfoClass[] = {
/* IoCompletionBasicInformation */
ICI_SQ_SAME( sizeof(IO_COMPLETION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ),
}; };
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
NTSTATUS VOID
STDCALL STDCALL
IopCreateIoCompletion(
PVOID ObjectBody,
PVOID Parent,
PWSTR RemainingPath,
POBJECT_ATTRIBUTES ObjectAttributes
)
{
DPRINT("IopCreateIoCompletion(ObjectBody %x, Parent %x, RemainingPath %S)\n",
ObjectBody, Parent, RemainingPath);
if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL)
{
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
VOID STDCALL
IopDeleteIoCompletion(PVOID ObjectBody) IopDeleteIoCompletion(PVOID ObjectBody)
{ {
PKQUEUE Queue = ObjectBody; PKQUEUE Queue = ObjectBody;
PLIST_ENTRY FirstEntry;
PLIST_ENTRY CurrentEntry;
PIO_COMPLETION_PACKET Packet;
DPRINT("IopDeleteIoCompletion()\n"); DPRINT("IopDeleteIoCompletion()\n");
KeRundownQueue(Queue); /* Rundown the Queue */
} FirstEntry = KeRundownQueue(Queue);
/* Clean up the IRPs */
/* if (FirstEntry) {
* @unimplemented
*/ CurrentEntry = FirstEntry;
NTSTATUS do {
STDCALL
IoSetCompletionRoutineEx( /* Get the Packet */
IN PDEVICE_OBJECT DeviceObject, Packet = CONTAINING_RECORD(CurrentEntry, IO_COMPLETION_PACKET, ListEntry);
IN PIRP Irp,
IN PIO_COMPLETION_ROUTINE CompletionRoutine, /* Go to next Entry */
IN PVOID Context, CurrentEntry = CurrentEntry->Flink;
IN BOOLEAN InvokeOnSuccess,
IN BOOLEAN InvokeOnError, /* Free it */
IN BOOLEAN InvokeOnCancel ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet);
) } while (FirstEntry != CurrentEntry);
{ }
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
} }
/* /*
@ -85,305 +72,412 @@ IoSetCompletionRoutineEx(
*/ */
NTSTATUS NTSTATUS
STDCALL STDCALL
IoSetIoCompletion ( IoSetIoCompletion(IN PVOID IoCompletion,
IN PVOID IoCompletion, IN PVOID KeyContext,
IN PVOID KeyContext, IN PVOID ApcContext,
IN PVOID ApcContext, IN NTSTATUS IoStatus,
IN NTSTATUS IoStatus, IN ULONG_PTR IoStatusInformation,
IN ULONG_PTR IoStatusInformation, IN BOOLEAN Quota)
IN BOOLEAN Quota
)
{ {
PKQUEUE Queue = (PKQUEUE) IoCompletion; PKQUEUE Queue = (PKQUEUE)IoCompletion;
PIO_COMPLETION_PACKET Packet; PIO_COMPLETION_PACKET Packet;
Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside); /* Allocate the Packet */
if (NULL == Packet) Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside);
{ if (NULL == Packet) return STATUS_NO_MEMORY;
return STATUS_NO_MEMORY;
} /* Set up the Packet */
Packet->Key = KeyContext;
Packet->Context = ApcContext;
Packet->IoStatus.Status = IoStatus;
Packet->IoStatus.Information = IoStatusInformation;
/* Insert the Queue */
KeInsertQueue(Queue, &Packet->ListEntry);
Packet->Key = KeyContext; /* Return Success */
Packet->Context = ApcContext; return STATUS_SUCCESS;
Packet->IoStatus.Status = IoStatus; }
Packet->IoStatus.Information = IoStatusInformation;
KeInsertQueue(Queue, &Packet->ListEntry);
return STATUS_SUCCESS; /*
* @unimplemented
*/
NTSTATUS
STDCALL
IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
IN PVOID Context,
IN BOOLEAN InvokeOnSuccess,
IN BOOLEAN InvokeOnError,
IN BOOLEAN InvokeOnCancel)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
} }
VOID VOID
FASTCALL FASTCALL
IopInitIoCompletionImplementation(VOID) IopInitIoCompletionImplementation(VOID)
{ {
ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); /* Create the IO Completion Type */
ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
RtlpCreateUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion", NonPagedPool); RtlInitUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion");
ExIoCompletionType->Tag = IOC_TAG;
ExIoCompletionType->Tag = IOC_TAG; ExIoCompletionType->PeakObjects = 0;
ExIoCompletionType->PeakObjects = 0; ExIoCompletionType->PeakHandles = 0;
ExIoCompletionType->PeakHandles = 0; ExIoCompletionType->TotalObjects = 0;
ExIoCompletionType->TotalObjects = 0; ExIoCompletionType->TotalHandles = 0;
ExIoCompletionType->TotalHandles = 0; ExIoCompletionType->PagedPoolCharge = 0;
ExIoCompletionType->PagedPoolCharge = 0; ExIoCompletionType->NonpagedPoolCharge = sizeof(KQUEUE);
ExIoCompletionType->NonpagedPoolCharge = sizeof(KQUEUE); ExIoCompletionType->Mapping = &ExIoCompletionMapping;
ExIoCompletionType->Mapping = &ExIoCompletionMapping; ExIoCompletionType->Dump = NULL;
ExIoCompletionType->Dump = NULL; ExIoCompletionType->Open = NULL;
ExIoCompletionType->Open = NULL; ExIoCompletionType->Close = NULL;
ExIoCompletionType->Close = NULL; ExIoCompletionType->Delete = IopDeleteIoCompletion;
ExIoCompletionType->Delete = IopDeleteIoCompletion; ExIoCompletionType->Parse = NULL;
ExIoCompletionType->Parse = NULL; ExIoCompletionType->Security = NULL;
ExIoCompletionType->Security = NULL; ExIoCompletionType->QueryName = NULL;
ExIoCompletionType->QueryName = NULL; ExIoCompletionType->OkayToClose = NULL;
ExIoCompletionType->OkayToClose = NULL; ExIoCompletionType->Create = NULL;
ExIoCompletionType->Create = IopCreateIoCompletion; ExIoCompletionType->DuplicationNotify = NULL;
ExIoCompletionType->DuplicationNotify = NULL;
ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside, /* Initialize the Lookaside List we'll use for packets */
NULL, ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside,
NULL, NULL,
0, NULL,
sizeof(IO_COMPLETION_PACKET), 0,
IOC_TAG, sizeof(IO_COMPLETION_PACKET),
0); IOC_TAG,
0);
} }
NTSTATUS NTSTATUS
STDCALL STDCALL
NtCreateIoCompletion( NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,
OUT PHANDLE IoCompletionHandle, IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes,
IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG NumberOfConcurrentThreads)
IN ULONG NumberOfConcurrentThreads
)
{ {
PKQUEUE Queue; PKQUEUE Queue;
NTSTATUS Status; HANDLE hIoCompletionHandle;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
Status = ObCreateObject(ExGetPreviousMode(), if (PreviousMode != KernelMode) {
ExIoCompletionType,
ObjectAttributes,
ExGetPreviousMode(),
NULL,
sizeof(KQUEUE),
0,
0,
(PVOID*)&Queue);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = ObInsertObject ((PVOID)Queue, _SEH_TRY {
NULL,
DesiredAccess,
0,
NULL,
IoCompletionHandle);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Queue);
return Status;
}
KeInitializeQueue(Queue, NumberOfConcurrentThreads); ProbeForWrite(IoCompletionHandle,
ObDereferenceObject(Queue); sizeof(HANDLE),
sizeof(ULONG));
} _SEH_HANDLE {
return STATUS_SUCCESS; Status = _SEH_GetExceptionCode();
/* } _SEH_END;
if (!NT_SUCCESS(Status)) {
CompletionPort = NULL OR ExistingCompletionPort return Status;
}
}
*/ /* Create the Object */
Status = ObCreateObject(PreviousMode,
ExIoCompletionType,
} ObjectAttributes,
PreviousMode,
/* NULL,
DesiredAccess: sizeof(KQUEUE),
ZERO 0,
IO_COMPLETION_QUERY_STATE Query access 0,
IO_COMPLETION_MODIFY_STATE Modify access (PVOID*)&Queue);
IO_COMPLETION_ALL_ACCESS All of the preceding + STANDARD_RIGHTS_ALL
/* Check for success */
ObjectAttributes if (NT_SUCCESS(Status)) {
OBJ_OPENLINK and OBJ_PERMANENT are not valid attributes
Return Value
STATUS_SUCCESS or an error status, such as STATUS_ACCESS_DENIED or
STATUS_OBJECT_NAME_NOT_FOUND.
*/
NTSTATUS
STDCALL
NtOpenIoCompletion(
OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
)
{
NTSTATUS Status;
Status = ObOpenObjectByName(ObjectAttributes, /* Initialize the Queue */
ExIoCompletionType, KeInitializeQueue(Queue, NumberOfConcurrentThreads);
NULL,
UserMode, /* Insert it */
DesiredAccess, Status = ObInsertObject(Queue,
NULL, NULL,
IoCompletionHandle); //<- ??? DesiredAccess,
0,
NULL,
&hIoCompletionHandle);
ObDereferenceObject(Queue);
if (NT_SUCCESS(Status)) {
_SEH_TRY {
*IoCompletionHandle = hIoCompletionHandle;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
}
/* Return Status */
return Status; return Status;
} }
NTSTATUS
STDCALL
NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
HANDLE hIoCompletionHandle;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
if(PreviousMode != KernelMode) {
_SEH_TRY {
ProbeForWrite(IoCompletionHandle,
sizeof(HANDLE),
sizeof(ULONG));
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
if(!NT_SUCCESS(Status)) {
return Status;
}
}
/* Open the Object */
Status = ObOpenObjectByName(ObjectAttributes,
ExIoCompletionType,
NULL,
PreviousMode,
DesiredAccess,
NULL,
&hIoCompletionHandle);
if (NT_SUCCESS(Status)) {
_SEH_TRY {
*IoCompletionHandle = hIoCompletionHandle;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
/* Return Status */
return Status;
}
NTSTATUS NTSTATUS
STDCALL STDCALL
NtQueryIoCompletion( NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
IN HANDLE IoCompletionHandle, IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, OUT PVOID IoCompletionInformation,
OUT PVOID IoCompletionInformation, IN ULONG IoCompletionInformationLength,
IN ULONG IoCompletionInformationLength, OUT PULONG ResultLength OPTIONAL)
OUT PULONG ResultLength OPTIONAL
)
{ {
PKQUEUE Queue; PKQUEUE Queue;
NTSTATUS Status; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
if (IoCompletionInformationClass != IoCompletionBasicInformation) /* Check buffers and parameters */
{ DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
return STATUS_INVALID_INFO_CLASS; ExIoCompletionInfoClass,
} IoCompletionInformation,
if (IoCompletionInformationLength < sizeof(IO_COMPLETION_BASIC_INFORMATION)) IoCompletionInformationLength,
{ ResultLength,
return STATUS_INFO_LENGTH_MISMATCH; PreviousMode,
} &Status);
if(!NT_SUCCESS(Status)) {
Status = ObReferenceObjectByHandle( IoCompletionHandle, DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status);
return Status;
}
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionHandle,
IO_COMPLETION_QUERY_STATE, IO_COMPLETION_QUERY_STATE,
ExIoCompletionType, ExIoCompletionType,
UserMode, PreviousMode,
(PVOID*)&Queue, (PVOID*)&Queue,
NULL); NULL);
if (NT_SUCCESS(Status))
{ /* Check for Success */
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = if (NT_SUCCESS(Status)) {
Queue->Header.SignalState;
_SEH_TRY {
ObDereferenceObject(Queue); /* Return Info */
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = KeReadStateQueue(Queue);
ObDereferenceObject(Queue);
if (ResultLength) *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION); /* Return Result Length if needed */
} if (ResultLength) {
return Status; *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
}
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
/* Return Status */
return Status;
} }
/* /*
* Dequeues an I/O completion message from an I/O completion object * Dequeues an I/O completion message from an I/O completion object
*/ */
NTSTATUS NTSTATUS
STDCALL STDCALL
NtRemoveIoCompletion( NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
IN HANDLE IoCompletionHandle, OUT PVOID *CompletionKey,
OUT PVOID *CompletionKey, OUT PVOID *CompletionContext,
OUT PVOID *CompletionContext, OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER Timeout OPTIONAL)
IN PLARGE_INTEGER Timeout OPTIONAL
)
{ {
PKQUEUE Queue; LARGE_INTEGER SafeTimeout;
NTSTATUS Status; PKQUEUE Queue;
PIO_COMPLETION_PACKET Packet; PIO_COMPLETION_PACKET Packet;
PLIST_ENTRY ListEntry; PLIST_ENTRY ListEntry;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
Status = ObReferenceObjectByHandle( IoCompletionHandle, if (PreviousMode != KernelMode) {
_SEH_TRY {
ProbeForWrite(CompletionKey,
sizeof(PVOID),
sizeof(ULONG));
ProbeForWrite(CompletionContext,
sizeof(PVOID),
sizeof(ULONG));
ProbeForWrite(IoStatusBlock,
sizeof(IO_STATUS_BLOCK),
sizeof(ULONG));
if (Timeout != NULL) {
ProbeForRead(Timeout,
sizeof(LARGE_INTEGER),
sizeof(ULONG));
SafeTimeout = *Timeout;
Timeout = &SafeTimeout;
}
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
if (!NT_SUCCESS(Status)) {
return Status;
}
}
/* Open the Object */
Status = ObReferenceObjectByHandle(IoCompletionHandle,
IO_COMPLETION_MODIFY_STATE, IO_COMPLETION_MODIFY_STATE,
ExIoCompletionType, ExIoCompletionType,
UserMode, PreviousMode,
(PVOID*)&Queue, (PVOID*)&Queue,
NULL); NULL);
if (!NT_SUCCESS(Status))
{ /* Check for success */
return Status; if (NT_SUCCESS(Status)) {
}
/* /* Remove queue */
Try 2 remove packet from queue. Wait (optionaly) if ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
no packet in queue or max num of threads allready running.
*/
do {
ListEntry = KeRemoveQueue(Queue, UserMode, Timeout );
/* Nebbets book says nothing about NtRemoveIoCompletion returning STATUS_USER_APC, /* If we got a timeout or user_apc back, return the status */
and the umode equivalent GetQueuedCompletionStatus says nothing about this either, if ((NTSTATUS)ListEntry == STATUS_TIMEOUT || (NTSTATUS)ListEntry == STATUS_USER_APC) {
so my guess it we should restart the operation. Need further investigation. -Gunnar
*/ Status = (NTSTATUS)ListEntry;
} else {
/* Get the Packet Data */
Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry);
_SEH_TRY {
} while((NTSTATUS)ListEntry == STATUS_USER_APC); /* Return it */
*CompletionKey = Packet->Key;
*CompletionContext = Packet->Context;
*IoStatusBlock = Packet->IoStatus;
} _SEH_HANDLE {
ObDereferenceObject(Queue); Status = _SEH_GetExceptionCode();
} _SEH_END;
/* Free packet */
ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet);
}
if ((NTSTATUS)ListEntry == STATUS_TIMEOUT) /* Dereference the Object */
{ ObDereferenceObject(Queue);
return STATUS_TIMEOUT; }
}
/* Return status */
ASSERT(ListEntry); return Status;
Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry);
if (CompletionKey) *CompletionKey = Packet->Key;
if (CompletionContext) *CompletionContext = Packet->Context;
if (IoStatusBlock) *IoStatusBlock = Packet->IoStatus;
ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet);
return STATUS_SUCCESS;
} }
/*
ASSOSIERT MED FOB's IoCompletionContext
typedef struct _IO_COMPLETION_CONTEXT {
PVOID Port;
ULONG Key;
} IO_COMPLETION_CONTEXT, *PIO_COMPLETION_CONTEXT;
*/
/* /*
* Queues an I/O completion message to an I/O completion object * Queues an I/O completion message to an I/O completion object
*/ */
NTSTATUS NTSTATUS
STDCALL STDCALL
NtSetIoCompletion( NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
IN HANDLE IoCompletionPortHandle, IN PVOID CompletionKey,
IN PVOID CompletionKey, IN PVOID CompletionContext,
IN PVOID CompletionContext, IN NTSTATUS CompletionStatus,
IN NTSTATUS CompletionStatus, IN ULONG CompletionInformation)
IN ULONG CompletionInformation
)
{ {
NTSTATUS Status; NTSTATUS Status;
PKQUEUE Queue; PKQUEUE Queue;
Status = ObReferenceObjectByHandle( IoCompletionPortHandle, PAGED_CODE();
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
IO_COMPLETION_MODIFY_STATE, IO_COMPLETION_MODIFY_STATE,
ExIoCompletionType, ExIoCompletionType,
UserMode, ExGetPreviousMode(),
(PVOID*)&Queue, (PVOID*)&Queue,
NULL); NULL);
if (NT_SUCCESS(Status))
{ /* Check for Success */
Status = IoSetIoCompletion(Queue, CompletionKey, CompletionContext, if (NT_SUCCESS(Status)) {
CompletionStatus, CompletionInformation, TRUE);
ObDereferenceObject(Queue); /* Set the Completion */
} Status = IoSetIoCompletion(Queue,
CompletionKey,
return Status; CompletionContext,
CompletionStatus,
CompletionInformation,
TRUE);
ObDereferenceObject(Queue);
}
/* Return status */
return Status;
} }