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
@ -28,56 +27,44 @@ static GENERIC_MAPPING ExIoCompletionMapping =
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) {
CurrentEntry = FirstEntry;
do {
/* Get the Packet */
Packet = CONTAINING_RECORD(CurrentEntry, IO_COMPLETION_PACKET, ListEntry);
/* Go to next Entry */
CurrentEntry = CurrentEntry->Flink;
/* Free it */
ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet);
} while (FirstEntry != CurrentEntry);
} }
/*
* @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;
} }
/* /*
@ -85,42 +72,57 @@ 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;
/* Allocate the Packet */
Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside); Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside);
if (NULL == Packet) if (NULL == Packet) return STATUS_NO_MEMORY;
{
return STATUS_NO_MEMORY;
}
/* Set up the Packet */
Packet->Key = KeyContext; Packet->Key = KeyContext;
Packet->Context = ApcContext; Packet->Context = ApcContext;
Packet->IoStatus.Status = IoStatus; Packet->IoStatus.Status = IoStatus;
Packet->IoStatus.Information = IoStatusInformation; Packet->IoStatus.Information = IoStatusInformation;
/* Insert the Queue */
KeInsertQueue(Queue, &Packet->ListEntry); KeInsertQueue(Queue, &Packet->ListEntry);
/* Return Success */
return STATUS_SUCCESS; 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)
{ {
/* Create the IO Completion Type */
ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
RtlInitUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion");
RtlpCreateUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion", NonPagedPool);
ExIoCompletionType->Tag = IOC_TAG; ExIoCompletionType->Tag = IOC_TAG;
ExIoCompletionType->PeakObjects = 0; ExIoCompletionType->PeakObjects = 0;
ExIoCompletionType->PeakHandles = 0; ExIoCompletionType->PeakHandles = 0;
@ -137,9 +139,10 @@ IopInitIoCompletionImplementation(VOID)
ExIoCompletionType->Security = NULL; ExIoCompletionType->Security = NULL;
ExIoCompletionType->QueryName = NULL; ExIoCompletionType->QueryName = NULL;
ExIoCompletionType->OkayToClose = NULL; ExIoCompletionType->OkayToClose = NULL;
ExIoCompletionType->Create = IopCreateIoCompletion; ExIoCompletionType->Create = NULL;
ExIoCompletionType->DuplicationNotify = NULL; ExIoCompletionType->DuplicationNotify = NULL;
/* Initialize the Lookaside List we'll use for packets */
ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside, ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside,
NULL, NULL,
NULL, NULL,
@ -149,241 +152,332 @@ IopInitIoCompletionImplementation(VOID)
0); 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;
Status = ObCreateObject(ExGetPreviousMode(), 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;
}
}
/* Create the Object */
Status = ObCreateObject(PreviousMode,
ExIoCompletionType, ExIoCompletionType,
ObjectAttributes, ObjectAttributes,
ExGetPreviousMode(), PreviousMode,
NULL, NULL,
sizeof(KQUEUE), sizeof(KQUEUE),
0, 0,
0, 0,
(PVOID*)&Queue); (PVOID*)&Queue);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = ObInsertObject ((PVOID)Queue, /* Check for success */
if (NT_SUCCESS(Status)) {
/* Initialize the Queue */
KeInitializeQueue(Queue, NumberOfConcurrentThreads);
/* Insert it */
Status = ObInsertObject(Queue,
NULL, NULL,
DesiredAccess, DesiredAccess,
0, 0,
NULL, NULL,
IoCompletionHandle); &hIoCompletionHandle);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Queue); ObDereferenceObject(Queue);
if (NT_SUCCESS(Status)) {
_SEH_TRY {
*IoCompletionHandle = hIoCompletionHandle;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
}
/* Return Status */
return Status; return Status;
} }
KeInitializeQueue(Queue, NumberOfConcurrentThreads);
ObDereferenceObject(Queue);
return STATUS_SUCCESS;
/*
CompletionPort = NULL OR ExistingCompletionPort
*/
}
/*
DesiredAccess:
ZERO
IO_COMPLETION_QUERY_STATE Query access
IO_COMPLETION_MODIFY_STATE Modify access
IO_COMPLETION_ALL_ACCESS All of the preceding + STANDARD_RIGHTS_ALL
ObjectAttributes
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 NTSTATUS
STDCALL STDCALL
NtOpenIoCompletion( NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes IN POBJECT_ATTRIBUTES ObjectAttributes)
)
{ {
NTSTATUS Status; 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, Status = ObOpenObjectByName(ObjectAttributes,
ExIoCompletionType, ExIoCompletionType,
NULL, NULL,
UserMode, PreviousMode,
DesiredAccess, DesiredAccess,
NULL, NULL,
IoCompletionHandle); //<- ??? &hIoCompletionHandle);
if (NT_SUCCESS(Status)) {
_SEH_TRY {
*IoCompletionHandle = hIoCompletionHandle;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
/* Return Status */
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;
if (IoCompletionInformationClass != IoCompletionBasicInformation) PAGED_CODE();
{
return STATUS_INVALID_INFO_CLASS;
}
if (IoCompletionInformationLength < sizeof(IO_COMPLETION_BASIC_INFORMATION))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
Status = ObReferenceObjectByHandle( IoCompletionHandle, /* Check buffers and parameters */
IO_COMPLETION_QUERY_STATE, DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
ExIoCompletionType, ExIoCompletionInfoClass,
UserMode, IoCompletionInformation,
(PVOID*)&Queue, IoCompletionInformationLength,
NULL); ResultLength,
if (NT_SUCCESS(Status)) PreviousMode,
{ &Status);
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = if(!NT_SUCCESS(Status)) {
Queue->Header.SignalState;
ObDereferenceObject(Queue);
if (ResultLength) *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
}
DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status);
return Status; return Status;
} }
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionHandle,
IO_COMPLETION_QUERY_STATE,
ExIoCompletionType,
PreviousMode,
(PVOID*)&Queue,
NULL);
/* Check for Success */
if (NT_SUCCESS(Status)) {
_SEH_TRY {
/* Return Info */
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = KeReadStateQueue(Queue);
ObDereferenceObject(Queue);
/* Return Result Length if needed */
if (ResultLength) {
*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)
)
{ {
LARGE_INTEGER SafeTimeout;
PKQUEUE Queue; PKQUEUE Queue;
NTSTATUS Status;
PIO_COMPLETION_PACKET Packet; PIO_COMPLETION_PACKET Packet;
PLIST_ENTRY ListEntry; PLIST_ENTRY ListEntry;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
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, 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))
{
return Status;
}
/* /* Check for success */
Try 2 remove packet from queue. Wait (optionaly) if if (NT_SUCCESS(Status)) {
no packet in queue or max num of threads allready running.
*/
do { /* Remove queue */
ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
ListEntry = KeRemoveQueue(Queue, UserMode, Timeout ); /* If we got a timeout or user_apc back, return the status */
if ((NTSTATUS)ListEntry == STATUS_TIMEOUT || (NTSTATUS)ListEntry == STATUS_USER_APC) {
/* Nebbets book says nothing about NtRemoveIoCompletion returning STATUS_USER_APC, Status = (NTSTATUS)ListEntry;
and the umode equivalent GetQueuedCompletionStatus says nothing about this either,
so my guess it we should restart the operation. Need further investigation. -Gunnar
*/
} while((NTSTATUS)ListEntry == STATUS_USER_APC); } else {
ObDereferenceObject(Queue);
if ((NTSTATUS)ListEntry == STATUS_TIMEOUT)
{
return STATUS_TIMEOUT;
}
ASSERT(ListEntry);
/* Get the Packet Data */
Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry); Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry);
if (CompletionKey) *CompletionKey = Packet->Key; _SEH_TRY {
if (CompletionContext) *CompletionContext = Packet->Context;
if (IoStatusBlock) *IoStatusBlock = Packet->IoStatus;
/* Return it */
*CompletionKey = Packet->Key;
*CompletionContext = Packet->Context;
*IoStatusBlock = Packet->IoStatus;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
/* Free packet */
ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet); ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet);
return STATUS_SUCCESS;
} }
/* Dereference the Object */
ObDereferenceObject(Queue);
}
/* /* Return status */
ASSOSIERT MED FOB's IoCompletionContext return Status;
}
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;
PAGED_CODE();
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionPortHandle, 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);
/* Set the Completion */
Status = IoSetIoCompletion(Queue,
CompletionKey,
CompletionContext,
CompletionStatus,
CompletionInformation,
TRUE);
ObDereferenceObject(Queue); ObDereferenceObject(Queue);
} }
/* Return status */
return Status; return Status;
} }