mirror of
https://github.com/reactos/reactos.git
synced 2024-09-10 12:54:51 +00:00
676 lines
21 KiB
C
676 lines
21 KiB
C
/*
|
|
* 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 <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* TYPES *********************************************************************/
|
|
|
|
typedef struct _IOP_ERROR_LOG_WORKER_DPC
|
|
{
|
|
KDPC Dpc;
|
|
KTIMER Timer;
|
|
} IOP_ERROR_LOG_WORKER_DPC, *PIOP_ERROR_LOG_WORKER_DPC;
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
LONG IopTotalLogSize;
|
|
LIST_ENTRY IopErrorLogListHead;
|
|
KSPIN_LOCK IopLogListLock;
|
|
|
|
BOOLEAN IopLogWorkerRunning;
|
|
BOOLEAN IopLogPortConnected;
|
|
HANDLE IopLogPort;
|
|
WORK_QUEUE_ITEM IopErrorLogWorkItem;
|
|
|
|
PDEVICE_OBJECT IopErrorLogObject;
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
VOID
|
|
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(&IopErrorLogListHead))
|
|
{
|
|
/* List is empty, disable the worker and return NULL */
|
|
IopLogWorkerRunning = FALSE;
|
|
ListEntry = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, remove an entry */
|
|
ListEntry = RemoveHeadList(&IopErrorLogListHead);
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
/* Remember 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 */
|
|
RtlCopyMemory(&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 = ALIGN_UP_POINTER(StringBuffer, WCHAR);
|
|
|
|
/* Set the offset for the driver's name to the current buffer */
|
|
ErrorMessage->DriverNameOffset = (ULONG)(StringBuffer -
|
|
(PCHAR)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;
|
|
}
|
|
else
|
|
DriverNameString.Buffer = NULL;
|
|
|
|
/* 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 = (ULONG)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));
|
|
RtlCopyMemory(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(Buffer),
|
|
&ReturnedLength);
|
|
if (!NT_SUCCESS(Status) || (ObjectNameInfo->Name.Length == 0))
|
|
{
|
|
/* 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 - (USHORT)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));
|
|
RtlCopyMemory(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);
|
|
PoolObjectNameInfo = NULL;
|
|
ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer;
|
|
}
|
|
|
|
/* 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 */
|
|
RtlCopyMemory(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(&IopErrorLogListHead,
|
|
&LogEntry->ListEntry,
|
|
&IopLogListLock);
|
|
IopLogWorkerRunning = FALSE;
|
|
IopRestartLogWorker();
|
|
break;
|
|
}
|
|
|
|
/* Dereference the device object */
|
|
if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject);
|
|
if (DriverObject) ObDereferenceObject(LogEntry->DriverObject);
|
|
|
|
/* Update size */
|
|
InterlockedExchangeAdd(&IopTotalLogSize,
|
|
-(LONG)(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
|
|
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;
|
|
//PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument2;
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
/* FIXME: UNIMPLEMENTED */
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PVOID
|
|
NTAPI
|
|
IoAllocateErrorLogEntry(IN PVOID IoObject,
|
|
IN UCHAR EntrySize)
|
|
{
|
|
PERROR_LOG_ENTRY LogEntry;
|
|
ULONG LogEntrySize;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PDRIVER_OBJECT DriverObject;
|
|
|
|
/* Make sure we have an object */
|
|
if (!IoObject) return NULL;
|
|
|
|
/* Check if we're past our buffer */
|
|
if (IopTotalLogSize > PAGE_SIZE) 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't have a device */
|
|
DeviceObject = NULL;
|
|
DriverObject = (PDRIVER_OBJECT)IoObject;
|
|
}
|
|
else
|
|
{
|
|
/* Fail */
|
|
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;
|
|
|
|
/* 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
|
|
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,
|
|
-(LONG)(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 */
|
|
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(&IopErrorLogListHead, &LogEntry->ListEntry);
|
|
|
|
/* Check if the worker is running */
|
|
if (!IopLogWorkerRunning)
|
|
{
|
|
#if 0
|
|
/* It's not, initialize it and queue it */
|
|
ExInitializeWorkItem(&IopErrorLogWorkItem,
|
|
IopLogWorker,
|
|
&IopErrorLogWorkItem);
|
|
ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue);
|
|
IopLogWorkerRunning = TRUE;
|
|
#endif
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* Don't do anything if hard errors are disabled on the thread */
|
|
if (Thread->HardErrorsAreDisabled)
|
|
{
|
|
/* Complete the request */
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return;
|
|
}
|
|
|
|
/* Setup an APC */
|
|
ErrorApc = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(KAPC),
|
|
TAG_APC);
|
|
KeInitializeApc(ErrorApc,
|
|
&Thread->Tcb,
|
|
Irp->ApcEnvironment,
|
|
NULL,
|
|
(PKRUNDOWN_ROUTINE)IopFreeApc,
|
|
(PKNORMAL_ROUTINE)IopRaiseHardError,
|
|
KernelMode,
|
|
Irp);
|
|
|
|
/* Queue an APC to deal with the error (see osr documentation) */
|
|
KeInsertQueueApc(ErrorApc, Vpb, RealDeviceObject, 0);
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
IoRaiseInformationalHardError(IN NTSTATUS ErrorStatus,
|
|
IN PUNICODE_STRING String,
|
|
IN PKTHREAD Thread)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled)
|
|
{
|
|
PETHREAD Thread = PsGetCurrentThread();
|
|
BOOLEAN OldMode;
|
|
|
|
/* Get the current value */
|
|
OldMode = !Thread->HardErrorsAreDisabled;
|
|
|
|
/* Set the new one and return the old */
|
|
Thread->HardErrorsAreDisabled = !HardErrorEnabled;
|
|
return OldMode;
|
|
}
|