From c31f4054ecf3340ff058aee051de13b67bb4b1ec Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Sat, 1 Jul 2006 03:36:15 +0000 Subject: [PATCH] - Add some missing IO_ERROR definitions to the DDK and add some tags - Fix IoSetThreadHardErrorMode... it was reading the TEB instead of the PETHREAD. - Optimize Error Logging: Use a static work item instead of allocating one each time, and don't use a spinlock for the buffer count, when we can use interlocked functions instead. - Log Entries can have Device AND/OR Driver Objects, not just a single one. They must also be referenced/dereferenced on allocation/free. - Rewrite IopLogWorker to properly deal with Device/Driver objects and querying their names, as well as with additional strings that the caller might be sending. svn path=/trunk/; revision=22734 --- reactos/include/ddk/winddk.h | 8 + reactos/ntoskrnl/include/internal/io.h | 33 +- reactos/ntoskrnl/include/internal/tag.h | 3 + reactos/ntoskrnl/io/error.c | 1054 +++++++++++++---------- 4 files changed, 628 insertions(+), 470 deletions(-) diff --git a/reactos/include/ddk/winddk.h b/reactos/include/ddk/winddk.h index ee3e711a152..418e1d03f17 100644 --- a/reactos/include/ddk/winddk.h +++ b/reactos/include/ddk/winddk.h @@ -1849,6 +1849,14 @@ typedef struct _IO_ERROR_LOG_MESSAGE { #define IO_ERROR_LOG_MESSAGE_HEADER_LENGTH (sizeof(IO_ERROR_LOG_MESSAGE) - \ sizeof(IO_ERROR_LOG_PACKET) + \ (sizeof(WCHAR) * 40)) +#define ERROR_LOG_MESSAGE_LIMIT_SIZE \ + (ERROR_LOG_LIMIT_SIZE + IO_ERROR_LOG_MESSAGE_HEADER_LENGTH) +#define IO_ERROR_LOG_MESSAGE_LENGTH \ + ((PORT_MAXIMUM_MESSAGE_LENGTH > ERROR_LOG_MESSAGE_LIMIT_SIZE) ? \ + ERROR_LOG_MESSAGE_LIMIT_SIZE : \ + PORT_MAXIMUM_MESSAGE_LENGTH) +#define ERROR_LOG_MAXIMUM_SIZE (IO_ERROR_LOG_MESSAGE_LENGTH - \ + IO_ERROR_LOG_MESSAGE_HEADER_LENGTH) typedef struct _CONTROLLER_OBJECT { CSHORT Type; diff --git a/reactos/ntoskrnl/include/internal/io.h b/reactos/ntoskrnl/include/internal/io.h index 53a2cf9677b..711d567f7a0 100644 --- a/reactos/ntoskrnl/include/internal/io.h +++ b/reactos/ntoskrnl/include/internal/io.h @@ -218,6 +218,29 @@ typedef struct _IO_INTERRUPT KSPIN_LOCK SpinLock; } IO_INTERRUPT, *PIO_INTERRUPT; +// +// I/O Error Log Packet Header +// +typedef struct _ERROR_LOG_ENTRY +{ + CSHORT Type; + CSHORT Size; + LIST_ENTRY ListEntry; + PDEVICE_OBJECT DeviceObject; + PDRIVER_OBJECT DriverObject; + LARGE_INTEGER TimeStamp; +} ERROR_LOG_ENTRY, *PERROR_LOG_ENTRY; + +// +// Event Log LPC Message +// +typedef struct _ELF_API_MSG +{ + PORT_MESSAGE h; + ULONG Unknown[2]; + IO_ERROR_LOG_MESSAGE IoErrorMessage; +} ELF_API_MSG, *PELF_API_MSG; + // // To simplify matters, the kernel is made to support both the checked and free // version of the I/O Remove Lock in the same binary. This structure includes @@ -604,12 +627,18 @@ IoInitCancelHandling( // // Error Logging Routines // - -NTSTATUS +VOID +NTAPI IopInitErrorLog( VOID ); +VOID +NTAPI +IopLogWorker( + IN PVOID Parameter +); + // // Raw File System MiniDriver // diff --git a/reactos/ntoskrnl/include/internal/tag.h b/reactos/ntoskrnl/include/internal/tag.h index a53528fc934..79d8922f40b 100644 --- a/reactos/ntoskrnl/include/internal/tag.h +++ b/reactos/ntoskrnl/include/internal/tag.h @@ -49,6 +49,9 @@ #define IO_SMALLIRP_CPU TAG('I', 'r', 'p', 'S') #define IOC_TAG1 TAG('I', 'p', 'c', ' ') #define IOC_CPU TAG('I', 'p', 'c', 'P') +#define TAG_APC TAG('K', 'A', 'P', 'C') +#define TAG_IO TAG('I', 'o', ' ', ' ') +#define TAG_ERROR_LOG TAG('I', 'o', 'E', 'r') /* formerly located in io/work.c */ #define TAG_IOWI TAG('I', 'O', 'W', 'I') diff --git a/reactos/ntoskrnl/io/error.c b/reactos/ntoskrnl/io/error.c index 0fdb013bc13..b239d149459 100644 --- a/reactos/ntoskrnl/io/error.c +++ b/reactos/ntoskrnl/io/error.c @@ -1,13 +1,11 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/io/errlog.c - * PURPOSE: Error logging - * - * PROGRAMMERS: David Welch (welch@cwcom.net) +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL - See COPYING in the top level directory + * FILE: ntoskrnl/io/error.c + * PURPOSE: I/O Error Functions and Error Log Support + * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Eric Kohl */ - /* INCLUDES *****************************************************************/ #include @@ -16,456 +14,457 @@ /* TYPES *********************************************************************/ -typedef struct _ERROR_LOG_ENTRY +typedef struct _IOP_ERROR_LOG_WORKER_DPC { - LIST_ENTRY Entry; - LARGE_INTEGER TimeStamp; - PVOID IoObject; - ULONG PacketSize; -} ERROR_LOG_ENTRY, *PERROR_LOG_ENTRY; - -typedef struct _LOG_WORKER_DPC -{ - KDPC Dpc; - KTIMER Timer; -} LOG_WORKER_DPC, *PLOG_WORKER_DPC; - - -static VOID STDCALL -IopLogWorker (PVOID Parameter); - + KDPC Dpc; + KTIMER Timer; +} IOP_ERROR_LOG_WORKER_DPC, *PIOP_ERROR_LOG_WORKER_DPC; /* GLOBALS *******************************************************************/ -static KSPIN_LOCK IopAllocationLock; -static ULONG IopTotalLogSize; +LONG IopTotalLogSize; +LIST_ENTRY IopLogListHead; +KSPIN_LOCK IopLogListLock; -static KSPIN_LOCK IopLogListLock; -static LIST_ENTRY IopLogListHead; +BOOLEAN IopLogWorkerRunning; +BOOLEAN IopLogPortConnected; +HANDLE IopLogPort; +WORK_QUEUE_ITEM IopErrorLogWorkItem; -static BOOLEAN IopLogWorkerRunning = FALSE; -static BOOLEAN IopLogPortConnected = FALSE; -static HANDLE IopLogPort; +/* PRIVATE FUNCTIONS *********************************************************/ - -/* FUNCTIONS *****************************************************************/ - -NTSTATUS -IopInitErrorLog (VOID) +VOID +NTAPI +IopInitErrorLog(VOID) { - IopTotalLogSize = 0; - KeInitializeSpinLock (&IopAllocationLock); - - KeInitializeSpinLock (&IopLogListLock); - InitializeListHead (&IopLogListHead); - - return STATUS_SUCCESS; -} - - -static VOID STDCALL -IopLogDpcRoutine (PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2) -{ - PWORK_QUEUE_ITEM LogWorkItem; - - DPRINT ("\nIopLogDpcRoutine() called\n"); - - /* Release the WorkerDpc struct */ - ExFreePool (DeferredContext); - - /* Allocate, initialize and restart a work item */ - LogWorkItem = ExAllocatePool (NonPagedPool, - sizeof(WORK_QUEUE_ITEM)); - if (LogWorkItem == NULL) - { - IopLogWorkerRunning = FALSE; - return; - } - - ExInitializeWorkItem (LogWorkItem, - IopLogWorker, - LogWorkItem); - - ExQueueWorkItem (LogWorkItem, - DelayedWorkQueue); -} - - -static VOID -IopRestartLogWorker (VOID) -{ - PLOG_WORKER_DPC WorkerDpc; - LARGE_INTEGER Timeout; - - DPRINT ("IopRestartWorker() called\n"); - - WorkerDpc = ExAllocatePool (NonPagedPool, - sizeof(LOG_WORKER_DPC)); - if (WorkerDpc == NULL) - { - IopLogWorkerRunning = FALSE; - return; - } - - /* Initialize DPC and Timer */ - KeInitializeDpc (&WorkerDpc->Dpc, - IopLogDpcRoutine, - WorkerDpc); - KeInitializeTimer (&WorkerDpc->Timer); - - /* Restart after 30 seconds */ - Timeout.QuadPart = (LONGLONG)-300000000; - KeSetTimer (&WorkerDpc->Timer, - Timeout, - &WorkerDpc->Dpc); -} - - -static BOOLEAN -IopConnectLogPort (VOID) -{ - UNICODE_STRING PortName; - NTSTATUS Status; - - DPRINT ("IopConnectLogPort() called\n"); - - RtlInitUnicodeString (&PortName, - L"\\ErrorLogPort"); - - Status = ZwConnectPort (&IopLogPort, - &PortName, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT ("ZwConnectPort() failed (Status %lx)\n", Status); - return FALSE; - } - - DPRINT ("IopConnectLogPort() done\n"); - - return TRUE; -} - - -static VOID STDCALL -IopLogWorker (PVOID Parameter) -{ - PERROR_LOG_ENTRY LogEntry; - PPORT_MESSAGE Request; - PIO_ERROR_LOG_MESSAGE Message; - PIO_ERROR_LOG_PACKET Packet; - KIRQL Irql; - NTSTATUS Status; - - UCHAR Buffer[256]; - POBJECT_NAME_INFORMATION ObjectNameInfo; - ULONG ReturnedLength; - PWCHAR DriverName; - ULONG DriverNameLength; - - DPRINT ("IopLogWorker() called\n"); - - /* Release the work item */ - ExFreePool (Parameter); - - /* Connect to the error log port */ - if (IopLogPortConnected == FALSE) - { - if (IopConnectLogPort () == FALSE) - { - IopRestartLogWorker (); - return; - } - - IopLogPortConnected = TRUE; - } - - while (TRUE) - { - /* Remove last entry from the list */ - KeAcquireSpinLock (&IopLogListLock, - &Irql); - - if (!IsListEmpty (&IopLogListHead)) - { - LogEntry = CONTAINING_RECORD (IopLogListHead.Blink, - ERROR_LOG_ENTRY, - Entry); - RemoveEntryList (&LogEntry->Entry); - } - else - { - LogEntry = NULL; - } - - KeReleaseSpinLock (&IopLogListLock, - Irql); - - if (LogEntry == NULL) - { - DPRINT ("No message in log list\n"); - break; - } - - /* Get pointer to the log packet */ - Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY)); - - - /* Get driver or device name */ - ObjectNameInfo = (POBJECT_NAME_INFORMATION)Buffer; - Status = ObQueryNameString (LogEntry->IoObject, - ObjectNameInfo, - 256, - &ReturnedLength); - if (NT_SUCCESS(Status)) - { - DPRINT ("ReturnedLength: %lu\n", ReturnedLength); - DPRINT ("Length: %hu\n", ObjectNameInfo->Name.Length); - DPRINT ("MaximumLength: %hu\n", ObjectNameInfo->Name.MaximumLength); - DPRINT ("Object: %wZ\n", &ObjectNameInfo->Name); - - DriverName = wcsrchr(ObjectNameInfo->Name.Buffer, L'\\'); - if (DriverName != NULL) - DriverName++; - else - DriverName = ObjectNameInfo->Name.Buffer; - - DriverNameLength = wcslen (DriverName) * sizeof(WCHAR); - DPRINT ("Driver name '%S'\n", DriverName); - } - else - { - DriverName = NULL; - DriverNameLength = 0; - } - - /* Allocate request buffer */ - Request = ExAllocatePool (NonPagedPool, - sizeof(PORT_MESSAGE) + PORT_MAXIMUM_MESSAGE_LENGTH); - if (Request == NULL) - { - DPRINT ("Failed to allocate request buffer!\n"); - - /* Requeue log message and restart the worker */ - ExInterlockedInsertTailList (&IopLogListHead, - &LogEntry->Entry, - &IopLogListLock); - IopRestartLogWorker (); - - return; - } - - /* Initialize the log message */ - Message = (PIO_ERROR_LOG_MESSAGE)(Request + 1); - Message->Type = IO_TYPE_ERROR_MESSAGE; - Message->Size = - sizeof(IO_ERROR_LOG_MESSAGE) - sizeof(IO_ERROR_LOG_PACKET) + - LogEntry->PacketSize + DriverNameLength; - Message->DriverNameLength = (USHORT)DriverNameLength; - Message->TimeStamp.QuadPart = LogEntry->TimeStamp.QuadPart; - Message->DriverNameOffset = (DriverName != NULL) ? LogEntry->PacketSize : 0; - - /* Copy error log packet */ - RtlCopyMemory (&Message->EntryData, - Packet, - LogEntry->PacketSize); - - /* Copy driver or device name */ - RtlCopyMemory ((PVOID)((ULONG_PTR)Message + Message->DriverNameOffset), - DriverName, - DriverNameLength); - - DPRINT ("SequenceNumber %lx\n", Packet->SequenceNumber); - - Request->u1.s1.DataLength = Message->Size; - Request->u1.s1.TotalLength = - Request->u1.s1.DataLength + sizeof(PPORT_MESSAGE); - - /* Send the error message to the log port */ - Status = ZwRequestPort (IopLogPort, - Request); - - /* Release request buffer */ - ExFreePool (Request); - - if (!NT_SUCCESS(Status)) - { - DPRINT ("ZwRequestPort() failed (Status %lx)\n", Status); - - /* Requeue log message and restart the worker */ - ExInterlockedInsertTailList (&IopLogListHead, - &LogEntry->Entry, - &IopLogListLock); - IopRestartLogWorker (); - - return; - } - - /* Release error log entry */ - KeAcquireSpinLock (&IopAllocationLock, - &Irql); - - IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY)); - ExFreePool (LogEntry); - - KeReleaseSpinLock (&IopAllocationLock, - Irql); - } - - IopLogWorkerRunning = FALSE; - - DPRINT ("IopLogWorker() done\n"); -} - - -/* - * @implemented - */ -PVOID STDCALL -IoAllocateErrorLogEntry (IN PVOID IoObject, - IN UCHAR EntrySize) -{ - PERROR_LOG_ENTRY LogEntry; - ULONG LogEntrySize; - KIRQL Irql; - - DPRINT("IoAllocateErrorLogEntry() called\n"); - - if (IoObject == NULL) - return NULL; - - KeAcquireSpinLock (&IopAllocationLock, - &Irql); - - if (IopTotalLogSize > PAGE_SIZE) - { - KeReleaseSpinLock (&IopAllocationLock, - Irql); - return NULL; - } - - LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize; - LogEntry = ExAllocatePool (NonPagedPool, - LogEntrySize); - if (LogEntry == NULL) - { - KeReleaseSpinLock (&IopAllocationLock, - Irql); - return NULL; - } - - IopTotalLogSize += EntrySize; - - LogEntry->IoObject = IoObject; - LogEntry->PacketSize = LogEntrySize; - - KeReleaseSpinLock (&IopAllocationLock, - Irql); - - return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY)); -} - - -/* - * @implemented - */ -VOID STDCALL -IoFreeErrorLogEntry(IN PVOID ElEntry) -{ - PERROR_LOG_ENTRY LogEntry; - KIRQL Irql; - - DPRINT("IoFreeErrorLogEntry() called\n"); - - if (ElEntry == NULL) - return; - - LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY)); - - KeAcquireSpinLock(&IopAllocationLock, - &Irql); - - IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY)); - ExFreePool(LogEntry); - - KeReleaseSpinLock(&IopAllocationLock, - Irql); -} - - -/* - * @implemented - */ -VOID STDCALL -IoWriteErrorLogEntry (IN PVOID ElEntry) -{ - PWORK_QUEUE_ITEM LogWorkItem; - PERROR_LOG_ENTRY LogEntry; - KIRQL Irql; - - DPRINT("IoWriteErrorLogEntry() called\n"); - - LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY)); - - /* Get time stamp */ - KeQuerySystemTime (&LogEntry->TimeStamp); - - KeAcquireSpinLock (&IopLogListLock, - &Irql); - - InsertHeadList (&IopLogListHead, - &LogEntry->Entry); - - if (IopLogWorkerRunning == FALSE) - { - LogWorkItem = ExAllocatePool (NonPagedPool, - sizeof(WORK_QUEUE_ITEM)); - if (LogWorkItem != NULL) - { - ExInitializeWorkItem (LogWorkItem, - IopLogWorker, - LogWorkItem); - - ExQueueWorkItem (LogWorkItem, - DelayedWorkQueue); - - IopLogWorkerRunning = TRUE; - } - } - - KeReleaseSpinLock (&IopLogListLock, - Irql); - - DPRINT("IoWriteErrorLogEntry() done\n"); + /* Initialize the locks and list head */ + KeInitializeSpinLock(&IopLogListLock); + InitializeListHead(&IopLogListHead); } VOID -STDCALL -IopFreeApc(PKAPC Apc, - PKNORMAL_ROUTINE *NormalRoutine, - PVOID *NormalContext, - PVOID *SystemArgument1, - PVOID *SystemArgument2) +NTAPI +IopLogDpcRoutine(IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + /* If we have a DPC, free it */ + if (Dpc) ExFreePool(Dpc); + + /* Initialize and queue the work item */ + ExInitializeWorkItem(&IopErrorLogWorkItem, IopLogWorker, NULL); + ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue); +} + +PLIST_ENTRY +NTAPI +IopGetErrorLogEntry(VOID) +{ + KIRQL OldIrql; + PLIST_ENTRY ListEntry; + + /* Acquire the lock and check if the list is empty */ + KeAcquireSpinLock(&IopLogListLock, &OldIrql); + if (IsListEmpty(&IopLogListHead)) + { + /* List is empty, disable the worker and return NULL */ + IopLogWorkerRunning = FALSE; + ListEntry = NULL; + } + else + { + /* Otherwise, remove an entry */ + ListEntry = RemoveHeadList(&IopLogListHead); + } + + /* Release the lock and return the entry */ + KeReleaseSpinLock(&IopLogListLock, OldIrql); + return ListEntry; +} + +VOID +NTAPI +IopRestartLogWorker(VOID) +{ + PIOP_ERROR_LOG_WORKER_DPC WorkerDpc; + LARGE_INTEGER Timeout; + + /* Allocate a DPC Context */ + WorkerDpc = ExAllocatePool(NonPagedPool, sizeof(IOP_ERROR_LOG_WORKER_DPC)); + if (!WorkerDpc) + { + /* Fail */ + IopLogWorkerRunning = FALSE; + return; + } + + /* Initialize DPC and Timer */ + KeInitializeDpc(&WorkerDpc->Dpc, IopLogDpcRoutine, WorkerDpc); + KeInitializeTimer(&WorkerDpc->Timer); + + /* Restart after 30 seconds */ + Timeout.QuadPart = (LONGLONG)-300000000; + KeSetTimer(&WorkerDpc->Timer, Timeout, &WorkerDpc->Dpc); +} + +BOOLEAN +NTAPI +IopConnectLogPort(VOID) +{ + UNICODE_STRING PortName = RTL_CONSTANT_STRING(L"\\ErrorLogPort"); + NTSTATUS Status; + + /* Make sure we're not already connected */ + if (IopLogPortConnected) return TRUE; + + /* Connect the port */ + Status = ZwConnectPort(&IopLogPort, + &PortName, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + if (NT_SUCCESS(Status)) + { + /* Remmeber we're connected */ + IopLogPortConnected = TRUE; + return TRUE; + } + + /* We failed, try again */ + IopRestartLogWorker(); + return FALSE; +} + +VOID +NTAPI +IopLogWorker(IN PVOID Parameter) +{ + PELF_API_MSG Message; + PIO_ERROR_LOG_MESSAGE ErrorMessage; + PLIST_ENTRY ListEntry; + PERROR_LOG_ENTRY LogEntry; + PIO_ERROR_LOG_PACKET Packet; + PCHAR StringBuffer; + ULONG RemainingLength; + PDRIVER_OBJECT DriverObject; + ULONG DriverNameLength = 0, DeviceNameLength; + UNICODE_STRING DriverNameString; + NTSTATUS Status; + UCHAR Buffer[256]; + POBJECT_NAME_INFORMATION ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer; + POBJECT_NAME_INFORMATION PoolObjectNameInfo = NULL; + ULONG ReturnedLength, MessageLength; + PWCHAR p; + ULONG ExtraStringLength; + PAGED_CODE(); + + /* Connect to the port */ + if (!IopConnectLogPort()) return; + + /* Allocate the message */ + Message = ExAllocatePool(PagedPool, IO_ERROR_LOG_MESSAGE_LENGTH); + if (!Message) + { + /* Couldn't allocate, try again */ + IopRestartLogWorker(); + return; + } + + /* Copy the message */ + RtlZeroMemory(Message, sizeof(ELF_API_MSG)); + + /* Get the actual I/O Structure */ + ErrorMessage = &Message->IoErrorMessage; + + /* Start loop */ + while (TRUE) + { + /* Get an entry */ + ListEntry = IopGetErrorLogEntry(); + if (!ListEntry) break; + LogEntry = CONTAINING_RECORD(ListEntry, ERROR_LOG_ENTRY, ListEntry); + + /* Get pointer to the log packet */ + Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry + + sizeof(ERROR_LOG_ENTRY)); + + /* Calculate the total length of the message only */ + MessageLength = sizeof(IO_ERROR_LOG_MESSAGE) - + sizeof(ERROR_LOG_ENTRY) - + sizeof(IO_ERROR_LOG_PACKET) + + LogEntry->Size; + + /* Copy the packet */ + RtlMoveMemory(&ErrorMessage->EntryData, + Packet, + LogEntry->Size - sizeof(ERROR_LOG_ENTRY)); + + /* Set the timestamp and time */ + ErrorMessage->TimeStamp = LogEntry->TimeStamp; + ErrorMessage->Type = IO_TYPE_ERROR_MESSAGE; + + /* Check if this message has any strings */ + if (Packet->NumberOfStrings) + { + /* String buffer is after the current strings */ + StringBuffer = (PCHAR)&ErrorMessage->EntryData + + Packet->StringOffset; + } + else + { + /* Otherwise, string buffer is at the end */ + StringBuffer = (PCHAR)ErrorMessage + MessageLength; + } + + /* Align the buffer */ + StringBuffer = (PVOID)ALIGN_UP(StringBuffer, WCHAR); + + /* Set the offset for the driver's name to the current buffer */ + ErrorMessage->DriverNameOffset = (ULONG)(StringBuffer - + (ULONG_PTR)ErrorMessage); + + /* Check how much space we have left for the device string */ + RemainingLength = (ULONG)((ULONG_PTR)Message + + IO_ERROR_LOG_MESSAGE_LENGTH - + (ULONG_PTR)StringBuffer); + + /* Now check if there is a driver object */ + DriverObject = LogEntry->DriverObject; + if (DriverObject) + { + /* Check if the driver has a name */ + if (DriverObject->DriverName.Buffer) + { + /* Use its name */ + DriverNameString.Buffer = DriverObject->DriverName.Buffer; + DriverNameLength = DriverObject->DriverName.Length; + } + + /* Check if there isn't a valid name*/ + if (!DriverNameLength) + { + /* Query the name directly */ + Status = ObQueryNameString(DriverObject, + ObjectNameInfo, + sizeof(Buffer), + &ReturnedLength); + if (!(NT_SUCCESS(Status)) || !(ObjectNameInfo->Name.Length)) + { + /* We don't have a name */ + DriverNameLength = 0; + } + } + } + else + { + /* Use default name */ + DriverNameString.Buffer = L"Application Popup"; + DriverNameLength = wcslen(DriverNameString.Buffer) * sizeof(WCHAR); + } + + /* Check if we have a driver name by here */ + if (DriverNameLength) + { + /* Skip to the end of the driver's name */ + p = &DriverNameString.Buffer[DriverNameLength / sizeof(WCHAR)]; + + /* Now we'll walk backwards and assume the minimum size */ + DriverNameLength = sizeof(WCHAR); + p--; + while ((*p != L'\\') && (p != DriverNameString.Buffer)) + { + /* No backslash found, keep going */ + p--; + DriverNameLength += sizeof(WCHAR); + } + + /* Now we probably hit the backslash itself, skip past it */ + if (*p == L'\\') + { + p++; + DriverNameLength -= sizeof(WCHAR); + } + + /* + * Now make sure that the driver name fits in our buffer, minus 3 + * NULL chars, and copy the name in our string buffer + */ + DriverNameLength = min(DriverNameLength, + RemainingLength - 3 * sizeof(UNICODE_NULL)); + RtlMoveMemory(StringBuffer, p, DriverNameLength); + } + + /* Null-terminate the driver name */ + *((PWSTR)(StringBuffer + DriverNameLength)) = L'\0'; + DriverNameLength += sizeof(WCHAR); + + /* Go to the next string buffer position */ + StringBuffer += DriverNameLength; + RemainingLength -= DriverNameLength; + + /* Update the string offset and check if we have a device object */ + ErrorMessage->EntryData.StringOffset = (USHORT) + ((ULONG_PTR)StringBuffer - + (ULONG_PTR)ErrorMessage); + if (LogEntry->DeviceObject) + { + /* We do, query its name */ + Status = ObQueryNameString(LogEntry->DeviceObject, + ObjectNameInfo, + sizeof(OBJECT_NAME_INFORMATION) + + 100 - + DriverNameLength, + &ReturnedLength); + if ((!NT_SUCCESS(Status)) || !(ObjectNameInfo->Name.Length)) + { + /* Setup an empty name */ + ObjectNameInfo->Name.Length = 0; + ObjectNameInfo->Name.Buffer = L""; + + /* Check if we failed because our buffer wasn't large enough */ + if (Status == STATUS_INFO_LENGTH_MISMATCH) + { + /* Then we'll allocate one... we really want this name! */ + PoolObjectNameInfo = ExAllocatePoolWithTag(PagedPool, + ReturnedLength, + TAG_IO); + if (PoolObjectNameInfo) + { + /* Query it again */ + ObjectNameInfo = PoolObjectNameInfo; + Status = ObQueryNameString(LogEntry->DeviceObject, + ObjectNameInfo, + ReturnedLength, + &ReturnedLength); + if (NT_SUCCESS(Status)) + { + /* Success, update the information */ + ObjectNameInfo->Name.Length = 100 - + DriverNameLength; + } + } + } + } + } + else + { + /* No device object, setup an empty name */ + ObjectNameInfo->Name.Length = 0; + ObjectNameInfo->Name.Buffer = L""; + } + + /* + * Now make sure that the device name fits in our buffer, minus 2 + * NULL chars, and copy the name in our string buffer + */ + DeviceNameLength = min(ObjectNameInfo->Name.Length, + RemainingLength - 2 * sizeof(UNICODE_NULL)); + RtlMoveMemory(StringBuffer, + ObjectNameInfo->Name.Buffer, + DeviceNameLength); + + /* Null-terminate the device name */ + *((PWSTR)(StringBuffer + DeviceNameLength)) = L'\0'; + DeviceNameLength += sizeof(WCHAR); + + /* Free the buffer if we had one */ + if (PoolObjectNameInfo) ExFreePool(PoolObjectNameInfo); + + /* Go to the next string buffer position */ + ErrorMessage->EntryData.NumberOfStrings++; + StringBuffer += DeviceNameLength; + RemainingLength -= DeviceNameLength; + + /* Check if we have any extra strings */ + if (Packet->NumberOfStrings) + { + /* Find out the size of the extra strings */ + ExtraStringLength = LogEntry->Size - + sizeof(ERROR_LOG_ENTRY) - + Packet->StringOffset; + + /* Make sure that the extra strings fit in our buffer */ + if (ExtraStringLength > (RemainingLength - sizeof(UNICODE_NULL))) + { + /* They wouldn't, so set normalize the length */ + MessageLength -= ExtraStringLength - RemainingLength; + ExtraStringLength = RemainingLength - sizeof(UNICODE_NULL); + } + + /* Now copy the extra strings */ + RtlMoveMemory(StringBuffer, + (PCHAR)Packet + Packet->StringOffset, + ExtraStringLength); + + /* Null-terminate them */ + *((PWSTR)(StringBuffer + ExtraStringLength)) = L'\0'; + } + + /* Set the driver name length */ + ErrorMessage->DriverNameLength = (USHORT)DriverNameLength; + + /* Update the message length to include the device and driver names */ + MessageLength += DeviceNameLength + DriverNameLength; + ErrorMessage->Size = (USHORT)MessageLength; + + /* Now update it again, internally, for the size of the actual LPC */ + MessageLength += (FIELD_OFFSET(ELF_API_MSG, IoErrorMessage) - + FIELD_OFFSET(ELF_API_MSG, Unknown[0])); + + /* Set the total and data lengths */ + Message->h.u1.s1.TotalLength = (USHORT)(sizeof(PORT_MESSAGE) + + MessageLength); + Message->h.u1.s1.DataLength = (USHORT)(MessageLength); + + /* Send the message */ + Status = NtRequestPort(IopLogPort, (PPORT_MESSAGE)Message); + if (!NT_SUCCESS(Status)) + { + /* Requeue log message and restart the worker */ + ExInterlockedInsertTailList(&IopLogListHead, + &LogEntry->ListEntry, + &IopLogListLock); + IopLogWorkerRunning = FALSE; + IopRestartLogWorker(); + break; + } + + /* Derefernece the device object */ + if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject); + if (DriverObject) ObDereferenceObject(LogEntry->DriverObject); + + /* Update size */ + InterlockedExchangeAdd(&IopTotalLogSize, + -(LogEntry->Size - sizeof(ERROR_LOG_ENTRY))); + } + + /* Free the LPC Message */ + ExFreePool(Message); +} + +VOID +NTAPI +IopFreeApc(IN PKAPC Apc, + IN PKNORMAL_ROUTINE *NormalRoutine, + IN PVOID *NormalContext, + IN PVOID *SystemArgument1, + IN PVOID *SystemArgument2) { /* Free the APC */ ExFreePool(Apc); } VOID -STDCALL -IopRaiseHardError(PKAPC Apc, - PKNORMAL_ROUTINE *NormalRoutine, - PVOID *NormalContext, - PVOID *SystemArgument1, - PVOID *SystemArgument2) +NTAPI +IopRaiseHardError(IN PKAPC Apc, + IN PKNORMAL_ROUTINE *NormalRoutine, + IN PVOID *NormalContext, + IN PVOID *SystemArgument1, + IN PVOID *SystemArgument2) { PIRP Irp = (PIRP)NormalContext; //PVPB Vpb = (PVPB)SystemArgument1; @@ -477,14 +476,141 @@ IopRaiseHardError(PKAPC Apc, IoCompleteRequest(Irp, IO_DISK_INCREMENT); } +/* PUBLIC FUNCTIONS **********************************************************/ + +/* + * @implemented + */ +PVOID +NTAPI +IoAllocateErrorLogEntry(IN PVOID IoObject, + IN UCHAR EntrySize) +{ + PERROR_LOG_ENTRY LogEntry; + ULONG LogEntrySize; + PDRIVER_OBJECT DriverObject; + PDEVICE_OBJECT DeviceObject; + + /* Make sure we have an object */ + if (!IoObject) return NULL; + + /* Check if we're past our buffer */ + if (IopTotalLogSize > PAGE_SIZE) return NULL; + + /* Calculate the total size and allocate it */ + LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize; + LogEntry = ExAllocatePoolWithTag(NonPagedPool, + LogEntrySize, + TAG_ERROR_LOG); + if (!LogEntry) return NULL; + + /* Check if this is a device object or driver object */ + if (((PDEVICE_OBJECT)IoObject)->Type == IO_TYPE_DEVICE) + { + /* It's a device, get the driver */ + DeviceObject = (PDEVICE_OBJECT)IoObject; + DriverObject = DeviceObject->DriverObject; + } + else if (((PDEVICE_OBJECT)IoObject)->Type == IO_TYPE_DRIVER) + { + /* It's a driver, so we don' thave a device */ + DeviceObject = NULL; + DriverObject = IoObject; + } + else + { + /* Fail */ + return NULL; + } + + /* Reference the Objects */ + if (DeviceObject) ObReferenceObject(DeviceObject); + if (DriverObject) ObReferenceObject(DriverObject); + + /* Update log size */ + InterlockedExchangeAdd(&IopTotalLogSize, EntrySize); + + /* Clear the entry and set it up */ + RtlZeroMemory(LogEntry, EntrySize); + LogEntry->Type = IO_TYPE_ERROR_LOG; + LogEntry->Size = EntrySize; + LogEntry->DeviceObject = DeviceObject; + LogEntry->DriverObject = DriverObject; + + /* Return the entry data */ + return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY)); +} + /* * @implemented */ VOID -STDCALL -IoRaiseHardError(PIRP Irp, - PVPB Vpb, - PDEVICE_OBJECT RealDeviceObject) +NTAPI +IoFreeErrorLogEntry(IN PVOID ElEntry) +{ + PERROR_LOG_ENTRY LogEntry; + + /* Make sure there's an entry */ + if (!ElEntry) return; + + /* Get the actual header */ + LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY)); + + /* Dereference both objects */ + if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject); + if (LogEntry->DriverObject) ObDereferenceObject(LogEntry->DriverObject); + + /* Decrease total allocation size and free the entry */ + InterlockedExchangeAdd(&IopTotalLogSize, + -(LogEntry->Size - sizeof(ERROR_LOG_ENTRY))); + ExFreePool(LogEntry); +} + +/* + * @implemented + */ +VOID +NTAPI +IoWriteErrorLogEntry(IN PVOID ElEntry) +{ + PERROR_LOG_ENTRY LogEntry; + KIRQL Irql; + + /* Get the main header */ + KEBUGCHECK(0); + LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - + sizeof(ERROR_LOG_ENTRY)); + + /* Get time stamp */ + KeQuerySystemTime(&LogEntry->TimeStamp); + + /* Acquire the lock and insert this write in the list */ + KeAcquireSpinLock(&IopLogListLock, &Irql); + InsertHeadList(&IopLogListHead, &LogEntry->ListEntry); + + /* Check if the worker is runnign */ + if (!IopLogWorkerRunning) + { + /* It's not, initialize it and queue it */ + ExInitializeWorkItem(&IopErrorLogWorkItem, + IopLogWorker, + &IopErrorLogWorkItem); + ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue); + IopLogWorkerRunning = TRUE; + } + + /* Release the lock and return */ + KeReleaseSpinLock(&IopLogListLock, Irql); +} + +/* + * @implemented + */ +VOID +NTAPI +IoRaiseHardError(IN PIRP Irp, + IN PVPB Vpb, + IN PDEVICE_OBJECT RealDeviceObject) { PETHREAD Thread = (PETHREAD)&Irp->Tail.Overlay.Thread; PKAPC ErrorApc; @@ -501,7 +627,7 @@ IoRaiseHardError(PIRP Irp, /* Setup an APC */ ErrorApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), - TAG('K', 'A', 'P', 'C')); + TAG_APC); KeInitializeApc(ErrorApc, &Thread->Tcb, Irp->ApcEnvironment, @@ -519,39 +645,31 @@ IoRaiseHardError(PIRP Irp, * @unimplemented */ BOOLEAN -STDCALL -IoRaiseInformationalHardError(NTSTATUS ErrorStatus, - PUNICODE_STRING String, - PKTHREAD Thread) +NTAPI +IoRaiseInformationalHardError(IN NTSTATUS ErrorStatus, + IN PUNICODE_STRING String, + IN PKTHREAD Thread) { UNIMPLEMENTED; return(FALSE); } -/********************************************************************** - * NAME EXPORTED - * IoSetThreadHardErrorMode@4 - * - * ARGUMENTS - * HardErrorEnabled - * TRUE : enable hard errors processing; - * FALSE: do NOT process hard errors. - * - * RETURN VALUE - * Previous value for the current thread's hard errors - * processing policy. - * +/* * @implemented */ BOOLEAN -STDCALL +NTAPI IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled) { - BOOLEAN PreviousHEM = (BOOLEAN)(NtCurrentTeb()->HardErrorDisabled); + PETHREAD Thread = PsGetCurrentThread(); + BOOLEAN Old; - NtCurrentTeb()->HardErrorDisabled = ((TRUE == HardErrorEnabled) ? FALSE : TRUE); + /* Get the current value */ + Old = !Thread->HardErrorsAreDisabled; - return((TRUE == PreviousHEM) ? FALSE : TRUE); + /* Set the new one and return the old */ + Thread->HardErrorsAreDisabled = !HardErrorEnabled; + return Old; } /* EOF */