mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
a330b56787
In addition to that, here are some stuff done in this commit whilst testing: - ICIF_QUERY_SIZE_VARIABLE and friends were badly misused, they should be used only when an information class whose information length size is dyanmic and not fixed. By removing such flags from erroneous classes, this fixes the STATUS_INFO_LENGTH_MISMATCH testcases. - Use CHAR instead of UCHAR for classes that do not need alignment probing, as every other class in the table do, for the sake of consistency. - ProcessEnableAlignmentFaultFixup uses BOOLEAN as type size, not CHAR. This fixes a testcase failure on ROS. - Check for information length size before proceeding further on querying the process' cookie information. - ProcessHandleTracing wants an alignment of a ULONG, not CHAR. - Move PROCESS_LDT_INFORMATION and PROCESS_LDT_SIZE outside of NTOS_MODE_USER macro case. This fixes a compilation issue when enabling the alignment probing. My mistake of having them inside NTOS_MODE_USER case, sorry. - On functions like NtQueryInformationThread and the Process equivalent, complete probing is not done at the beginning of the function, complete probing including if the buffer is writable alongside with datatype misalignment check that is. Instead such check is done on each information class case basis. With that said, we have to explicitly tell DefaultQueryInfoBufferCheck if we want a complete probing or not initially.
602 lines
18 KiB
C
602 lines
18 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/io/iomgr/iocomp.c
|
|
* PURPOSE: I/O Wrappers (called Completion Ports) for Kernel Queues
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Thomas Weidenmueller (w3seek@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
POBJECT_TYPE IoCompletionType;
|
|
|
|
GENERAL_LOOKASIDE IoCompletionPacketLookaside;
|
|
|
|
GENERIC_MAPPING IopCompletionMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE,
|
|
STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE,
|
|
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
|
|
IO_COMPLETION_ALL_ACCESS
|
|
};
|
|
|
|
static const INFORMATION_CLASS_INFO IoCompletionInfoClass[] =
|
|
{
|
|
/* IoCompletionBasicInformation */
|
|
IQS_SAME(IO_COMPLETION_BASIC_INFORMATION, ULONG, ICIF_QUERY),
|
|
};
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context)
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnsafeContext = Context;
|
|
|
|
/* Reference the device object */
|
|
ObReferenceObject(UnsafeContext->DeviceObject);
|
|
|
|
/* Call the completion routine */
|
|
Status= UnsafeContext->CompletionRoutine(DeviceObject,
|
|
Irp,
|
|
UnsafeContext->Context);
|
|
|
|
/* Dereference the device object */
|
|
ObDereferenceObject(UnsafeContext->DeviceObject);
|
|
|
|
/* Free our context */
|
|
ExFreePool(UnsafeContext);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet)
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
PNPAGED_LOOKASIDE_LIST List;
|
|
|
|
/* Use the P List */
|
|
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
|
|
PPLookasideList[LookasideCompletionList].P;
|
|
List->L.TotalFrees++;
|
|
|
|
/* Check if the Free was within the Depth or not */
|
|
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
|
|
{
|
|
/* Let the balancer know */
|
|
List->L.FreeMisses++;
|
|
|
|
/* Use the L List */
|
|
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
|
|
PPLookasideList[LookasideCompletionList].L;
|
|
List->L.TotalFrees++;
|
|
|
|
/* Check if the Free was within the Depth or not */
|
|
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
|
|
{
|
|
/* All lists failed, use the pool */
|
|
List->L.FreeMisses++;
|
|
ExFreePool(Packet);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* The free was within dhe Depth */
|
|
InterlockedPushEntrySList(&List->L.ListHead, (PSLIST_ENTRY)Packet);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopDeleteIoCompletion(PVOID ObjectBody)
|
|
{
|
|
PKQUEUE Queue = ObjectBody;
|
|
PLIST_ENTRY FirstEntry;
|
|
PLIST_ENTRY CurrentEntry;
|
|
PIRP Irp;
|
|
PIOP_MINI_COMPLETION_PACKET Packet;
|
|
|
|
/* Rundown the Queue */
|
|
FirstEntry = KeRundownQueue(Queue);
|
|
if (FirstEntry)
|
|
{
|
|
/* Loop the packets */
|
|
CurrentEntry = FirstEntry;
|
|
do
|
|
{
|
|
/* Get the Packet */
|
|
Packet = CONTAINING_RECORD(CurrentEntry,
|
|
IOP_MINI_COMPLETION_PACKET,
|
|
ListEntry);
|
|
|
|
/* Go to next Entry */
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
|
|
/* Check if it's part of an IRP, or a separate packet */
|
|
if (Packet->PacketType == IopCompletionPacketIrp)
|
|
{
|
|
/* Get the IRP and free it */
|
|
Irp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.ListEntry);
|
|
IoFreeIrp(Irp);
|
|
}
|
|
else
|
|
{
|
|
/* Use common routine */
|
|
IopFreeMiniPacket(Packet);
|
|
}
|
|
} while (FirstEntry != CurrentEntry);
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoSetIoCompletion(IN PVOID IoCompletion,
|
|
IN PVOID KeyContext,
|
|
IN PVOID ApcContext,
|
|
IN NTSTATUS IoStatus,
|
|
IN ULONG_PTR IoStatusInformation,
|
|
IN BOOLEAN Quota)
|
|
{
|
|
PKQUEUE Queue = (PKQUEUE)IoCompletion;
|
|
PNPAGED_LOOKASIDE_LIST List;
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
PIOP_MINI_COMPLETION_PACKET Packet;
|
|
|
|
/* Get the P List */
|
|
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
|
|
PPLookasideList[LookasideCompletionList].P;
|
|
|
|
/* Try to allocate the Packet */
|
|
List->L.TotalAllocates++;
|
|
Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
|
|
|
|
/* Check if that failed, use the L list if it did */
|
|
if (!Packet)
|
|
{
|
|
/* Let the balancer know */
|
|
List->L.AllocateMisses++;
|
|
|
|
/* Get L List */
|
|
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
|
|
PPLookasideList[LookasideCompletionList].L;
|
|
|
|
/* Try to allocate the Packet */
|
|
List->L.TotalAllocates++;
|
|
Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
|
|
}
|
|
|
|
/* Still failed, use pool */
|
|
if (!Packet)
|
|
{
|
|
/* Let the balancer know */
|
|
List->L.AllocateMisses++;
|
|
|
|
/* Allocate from Nonpaged Pool */
|
|
Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG);
|
|
}
|
|
|
|
/* Make sure we have one by now... */
|
|
if (Packet)
|
|
{
|
|
/* Set up the Packet */
|
|
Packet->PacketType = IopCompletionPacketMini;
|
|
Packet->KeyContext = KeyContext;
|
|
Packet->ApcContext = ApcContext;
|
|
Packet->IoStatus = IoStatus;
|
|
Packet->IoStatusInformation = IoStatusInformation;
|
|
|
|
/* Insert the Queue */
|
|
KeInsertQueue(Queue, &Packet->ListEntry);
|
|
}
|
|
else
|
|
{
|
|
/* Out of memory, fail */
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Return Success */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
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)
|
|
{
|
|
PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnloadContext;
|
|
|
|
/* Allocate the context */
|
|
UnloadContext = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(*UnloadContext),
|
|
'sUoI');
|
|
if (!UnloadContext) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Set up the context */
|
|
UnloadContext->DeviceObject = DeviceObject;
|
|
UnloadContext->Context = Context;
|
|
UnloadContext->CompletionRoutine = CompletionRoutine;
|
|
|
|
/* Now set the completion routine */
|
|
IoSetCompletionRoutine(Irp,
|
|
IopUnloadSafeCompletion,
|
|
UnloadContext,
|
|
InvokeOnSuccess,
|
|
InvokeOnError,
|
|
InvokeOnCancel);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ULONG NumberOfConcurrentThreads)
|
|
{
|
|
PKQUEUE Queue;
|
|
HANDLE hIoCompletionHandle;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Check if this was a user-mode call */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Wrap probing in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the handle */
|
|
ProbeForWriteHandle(IoCompletionHandle);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Create the Object */
|
|
Status = ObCreateObject(PreviousMode,
|
|
IoCompletionType,
|
|
ObjectAttributes,
|
|
PreviousMode,
|
|
NULL,
|
|
sizeof(KQUEUE),
|
|
0,
|
|
0,
|
|
(PVOID*)&Queue);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Initialize the Queue */
|
|
KeInitializeQueue(Queue, NumberOfConcurrentThreads);
|
|
|
|
/* Insert it */
|
|
Status = ObInsertObject(Queue,
|
|
NULL,
|
|
DesiredAccess,
|
|
0,
|
|
NULL,
|
|
&hIoCompletionHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Protect writing the handle in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the handle back */
|
|
*IoCompletionHandle = hIoCompletionHandle;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
}
|
|
|
|
/* Return Status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
HANDLE hIoCompletionHandle;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Check if this was a user-mode call */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Wrap probing in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the handle */
|
|
ProbeForWriteHandle(IoCompletionHandle);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Open the Object */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
IoCompletionType,
|
|
PreviousMode,
|
|
NULL,
|
|
DesiredAccess,
|
|
NULL,
|
|
&hIoCompletionHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Protect writing the handle in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the handle back */
|
|
*IoCompletionHandle = hIoCompletionHandle;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Return Status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
|
|
IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
|
|
OUT PVOID IoCompletionInformation,
|
|
IN ULONG IoCompletionInformationLength,
|
|
OUT PULONG ResultLength OPTIONAL)
|
|
{
|
|
PKQUEUE Queue;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Check buffers and parameters */
|
|
Status = DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
|
|
IoCompletionInfoClass,
|
|
sizeof(IoCompletionInfoClass) /
|
|
sizeof(IoCompletionInfoClass[0]),
|
|
IoCompletionInformation,
|
|
IoCompletionInformationLength,
|
|
ResultLength,
|
|
NULL,
|
|
PreviousMode,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get the Object */
|
|
Status = ObReferenceObjectByHandle(IoCompletionHandle,
|
|
IO_COMPLETION_QUERY_STATE,
|
|
IoCompletionType,
|
|
PreviousMode,
|
|
(PVOID*)&Queue,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return Info */
|
|
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->
|
|
Depth = KeReadStateQueue(Queue);
|
|
|
|
/* Return Result Length if needed */
|
|
if (ResultLength)
|
|
{
|
|
*ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the queue */
|
|
ObDereferenceObject(Queue);
|
|
}
|
|
|
|
/* Return Status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
|
|
OUT PVOID *KeyContext,
|
|
OUT PVOID *ApcContext,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL)
|
|
{
|
|
LARGE_INTEGER SafeTimeout;
|
|
PKQUEUE Queue;
|
|
PIOP_MINI_COMPLETION_PACKET Packet;
|
|
PLIST_ENTRY ListEntry;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
PVOID Apc, Key;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PAGED_CODE();
|
|
|
|
/* Check if the call was from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Protect probes in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the pointers */
|
|
ProbeForWritePointer(KeyContext);
|
|
ProbeForWritePointer(ApcContext);
|
|
|
|
/* Probe the I/O Status Block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
if (Timeout)
|
|
{
|
|
/* Probe and capture the timeout */
|
|
SafeTimeout = ProbeForReadLargeInteger(Timeout);
|
|
Timeout = &SafeTimeout;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Open the Object */
|
|
Status = ObReferenceObjectByHandle(IoCompletionHandle,
|
|
IO_COMPLETION_MODIFY_STATE,
|
|
IoCompletionType,
|
|
PreviousMode,
|
|
(PVOID*)&Queue,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Remove queue */
|
|
ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
|
|
|
|
/* If we got a timeout or user_apc back, return the status */
|
|
if (((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_TIMEOUT) ||
|
|
((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_USER_APC))
|
|
{
|
|
/* Set this as the status */
|
|
Status = (NTSTATUS)(ULONG_PTR)ListEntry;
|
|
}
|
|
else
|
|
{
|
|
/* Get the Packet Data */
|
|
Packet = CONTAINING_RECORD(ListEntry,
|
|
IOP_MINI_COMPLETION_PACKET,
|
|
ListEntry);
|
|
|
|
/* Check if this is piggybacked on an IRP */
|
|
if (Packet->PacketType == IopCompletionPacketIrp)
|
|
{
|
|
/* Get the IRP */
|
|
Irp = CONTAINING_RECORD(ListEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
/* Save values */
|
|
Key = Irp->Tail.CompletionKey;
|
|
Apc = Irp->Overlay.AsynchronousParameters.UserApcContext;
|
|
IoStatus = Irp->IoStatus;
|
|
|
|
/* Free the IRP */
|
|
IoFreeIrp(Irp);
|
|
}
|
|
else
|
|
{
|
|
/* Save values */
|
|
Key = Packet->KeyContext;
|
|
Apc = Packet->ApcContext;
|
|
IoStatus.Status = Packet->IoStatus;
|
|
IoStatus.Information = Packet->IoStatusInformation;
|
|
|
|
/* Free the packet */
|
|
IopFreeMiniPacket(Packet);
|
|
}
|
|
|
|
/* Enter SEH to write back the values */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the values to caller */
|
|
*ApcContext = Apc;
|
|
*KeyContext = Key;
|
|
*IoStatusBlock = IoStatus;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Dereference the Object */
|
|
ObDereferenceObject(Queue);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
|
|
IN PVOID CompletionKey,
|
|
IN PVOID CompletionContext,
|
|
IN NTSTATUS CompletionStatus,
|
|
IN ULONG CompletionInformation)
|
|
{
|
|
NTSTATUS Status;
|
|
PKQUEUE Queue;
|
|
PAGED_CODE();
|
|
|
|
/* Get the Object */
|
|
Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
|
|
IO_COMPLETION_MODIFY_STATE,
|
|
IoCompletionType,
|
|
ExGetPreviousMode(),
|
|
(PVOID*)&Queue,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Set the Completion */
|
|
Status = IoSetIoCompletion(Queue,
|
|
CompletionKey,
|
|
CompletionContext,
|
|
CompletionStatus,
|
|
CompletionInformation,
|
|
TRUE);
|
|
|
|
/* Dereference the object */
|
|
ObDereferenceObject(Queue);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|