/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/fsrtl/stackovf.c * PURPOSE: Provides Stack Overflow support for File System Drivers * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* GLOBALS *******************************************************************/ /* We have one queue for paging files, one queue for normal files * Queue 0 is for non-paging files * Queue 1 is for paging files * Don't add new/change current queues unless you know what you do * Most of the code relies on the fact that we have two queues in that order */ #define FSRTLP_MAX_QUEUES 2 typedef struct _STACK_OVERFLOW_WORK_ITEM { WORK_QUEUE_ITEM WorkItem; PFSRTL_STACK_OVERFLOW_ROUTINE Routine; PVOID Context; PKEVENT Event; } STACK_OVERFLOW_WORK_ITEM, *PSTACK_OVERFLOW_WORK_ITEM; KEVENT StackOverflowFallbackSerialEvent; STACK_OVERFLOW_WORK_ITEM StackOverflowFallback; KQUEUE FsRtlWorkerQueues[FSRTLP_MAX_QUEUES]; /* PRIVATE FUNCTIONS *********************************************************/ /* * @implemented */ VOID NTAPI FsRtlStackOverflowRead(IN PVOID Context) { PSTACK_OVERFLOW_WORK_ITEM WorkItem; WorkItem = (PSTACK_OVERFLOW_WORK_ITEM)Context; /* Put us as top IRP for current thread */ IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); /* And call FsRtlSORoutine */ WorkItem->Routine(WorkItem->Context, WorkItem->Event); /* If we were using fallback workitem, don't free it, just reset event */ if (WorkItem == &StackOverflowFallback) { KeSetEvent(&StackOverflowFallbackSerialEvent, 0, FALSE); } /* Otherwise, free the work item */ else { ExFreePoolWithTag(WorkItem, 'srSF'); } /* Reset top level */ IoSetTopLevelIrp(NULL); } /* * @implemented */ VOID NTAPI FsRtlpPostStackOverflow(IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine, IN BOOLEAN IsPaging) { PSTACK_OVERFLOW_WORK_ITEM WorkItem; /* Try to allocate a work item */ WorkItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(STACK_OVERFLOW_WORK_ITEM), 'srSF'); if (WorkItem == NULL) { /* If we failed, and we are not a paging file, just raise an error */ if (!IsPaging) { RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); } /* Otherwise, wait for fallback workitem to be available and use it */ KeWaitForSingleObject(&StackOverflowFallbackSerialEvent, Executive, KernelMode, FALSE, NULL); WorkItem = &StackOverflowFallback; } /* Initialize work item */ WorkItem->Context = Context; WorkItem->Event = Event; WorkItem->Routine = StackOverflowRoutine; ExInitializeWorkItem(&WorkItem->WorkItem, FsRtlStackOverflowRead, WorkItem); /* And queue it in the appropriate queue (paging or not?) */ KeInsertQueue(&FsRtlWorkerQueues[IsPaging], &WorkItem->WorkItem.List); } /* * @implemented */ VOID NTAPI FsRtlWorkerThread(IN PVOID StartContext) { KIRQL Irql; PLIST_ENTRY Entry; PWORK_QUEUE_ITEM WorkItem; ULONG_PTR QueueId = (ULONG_PTR)StartContext; /* Set our priority according to the queue we're dealing with */ KeSetPriorityThread(&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + QueueId); /* Loop for events */ for (;;) { /* Look for next event */ Entry = KeRemoveQueue(&FsRtlWorkerQueues[QueueId], KernelMode, NULL); WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); /* Call its routine (here: FsRtlStackOverflowRead) */ WorkItem->WorkerRoutine(WorkItem->Parameter); /* Check we're still at passive level or bugcheck */ Irql = KeGetCurrentIrql(); if (Irql != PASSIVE_LEVEL) { KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)Irql, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)WorkItem); } } } /* * @implemented */ CODE_SEG("INIT") NTSTATUS NTAPI FsRtlInitializeWorkerThread(VOID) { ULONG_PTR i; NTSTATUS Status; HANDLE ThreadHandle; OBJECT_ATTRIBUTES ObjectAttributes; /* Initialize each queue we have */ for (i = 0; i < FSRTLP_MAX_QUEUES; ++i) { InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); /* Initialize the queue and its associated thread and pass it the queue ID */ KeInitializeQueue(&FsRtlWorkerQueues[i], 0); Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, 0, 0, FsRtlWorkerThread, (PVOID)i); if (!NT_SUCCESS(Status)) { return Status; } /* Don't leak handle */ ZwClose(ThreadHandle); } /* Also initialize our fallback event, set it to ensure it's already usable */ KeInitializeEvent(&StackOverflowFallbackSerialEvent, SynchronizationEvent, TRUE); return Status; } /* PUBLIC FUNCTIONS **********************************************************/ /*++ * @name FsRtlPostPagingFileStackOverflow * @implemented NT 5.2 * * The FsRtlPostPagingFileStackOverflow routine * * @param Context * * @param Event * * @param StackOverflowRoutine * * @return * * @remarks None. * *--*/ VOID NTAPI FsRtlPostPagingFileStackOverflow(IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine) { FsRtlpPostStackOverflow(Context, Event, StackOverflowRoutine, TRUE); } /*++ * @name FsRtlPostStackOverflow * @implemented NT 5.2 * * The FsRtlPostStackOverflow routine * * @param Context * * @param Event * * @param StackOverflowRoutine * * @return * * @remarks None. * *--*/ VOID NTAPI FsRtlPostStackOverflow(IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine) { FsRtlpPostStackOverflow(Context, Event, StackOverflowRoutine, FALSE); } /* EOF */