2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/error.c
5 * PURPOSE: I/O Error Functions and Error Log Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *****************************************************************/
15 /* TYPES *********************************************************************/
17 typedef struct _IOP_ERROR_LOG_WORKER_DPC
21 } IOP_ERROR_LOG_WORKER_DPC
, *PIOP_ERROR_LOG_WORKER_DPC
;
23 /* GLOBALS *******************************************************************/
26 LIST_ENTRY IopErrorLogListHead
;
27 KSPIN_LOCK IopLogListLock
;
29 BOOLEAN IopLogWorkerRunning
;
30 BOOLEAN IopLogPortConnected
;
32 WORK_QUEUE_ITEM IopErrorLogWorkItem
;
34 PDEVICE_OBJECT IopErrorLogObject
;
36 /* PRIVATE FUNCTIONS *********************************************************/
40 IopLogDpcRoutine(IN PKDPC Dpc
,
41 IN PVOID DeferredContext
,
42 IN PVOID SystemArgument1
,
43 IN PVOID SystemArgument2
)
45 /* If we have a DPC, free it */
46 if (Dpc
) ExFreePool(Dpc
);
48 /* Initialize and queue the work item */
49 ExInitializeWorkItem(&IopErrorLogWorkItem
, IopLogWorker
, NULL
);
50 ExQueueWorkItem(&IopErrorLogWorkItem
, DelayedWorkQueue
);
55 IopGetErrorLogEntry(VOID
)
58 PLIST_ENTRY ListEntry
;
60 /* Acquire the lock and check if the list is empty */
61 KeAcquireSpinLock(&IopLogListLock
, &OldIrql
);
62 if (IsListEmpty(&IopErrorLogListHead
))
64 /* List is empty, disable the worker and return NULL */
65 IopLogWorkerRunning
= FALSE
;
70 /* Otherwise, remove an entry */
71 ListEntry
= RemoveHeadList(&IopErrorLogListHead
);
74 /* Release the lock and return the entry */
75 KeReleaseSpinLock(&IopLogListLock
, OldIrql
);
81 IopRestartLogWorker(VOID
)
83 PIOP_ERROR_LOG_WORKER_DPC WorkerDpc
;
84 LARGE_INTEGER Timeout
;
86 /* Allocate a DPC Context */
87 WorkerDpc
= ExAllocatePool(NonPagedPool
, sizeof(IOP_ERROR_LOG_WORKER_DPC
));
91 IopLogWorkerRunning
= FALSE
;
95 /* Initialize DPC and Timer */
96 KeInitializeDpc(&WorkerDpc
->Dpc
, IopLogDpcRoutine
, WorkerDpc
);
97 KeInitializeTimer(&WorkerDpc
->Timer
);
99 /* Restart after 30 seconds */
100 Timeout
.QuadPart
= (LONGLONG
)-300000000;
101 KeSetTimer(&WorkerDpc
->Timer
, Timeout
, &WorkerDpc
->Dpc
);
106 IopConnectLogPort(VOID
)
108 UNICODE_STRING PortName
= RTL_CONSTANT_STRING(L
"\\ErrorLogPort");
111 /* Make sure we're not already connected */
112 if (IopLogPortConnected
) return TRUE
;
114 /* Connect the port */
115 Status
= ZwConnectPort(&IopLogPort
,
123 if (NT_SUCCESS(Status
))
125 /* Remember we're connected */
126 IopLogPortConnected
= TRUE
;
130 /* We failed, try again */
131 IopRestartLogWorker();
137 IopLogWorker(IN PVOID Parameter
)
139 PELF_API_MSG Message
;
140 PIO_ERROR_LOG_MESSAGE ErrorMessage
;
141 PLIST_ENTRY ListEntry
;
142 PERROR_LOG_ENTRY LogEntry
;
143 PIO_ERROR_LOG_PACKET Packet
;
145 ULONG RemainingLength
;
146 PDRIVER_OBJECT DriverObject
;
147 ULONG DriverNameLength
= 0, DeviceNameLength
;
148 UNICODE_STRING DriverNameString
;
151 POBJECT_NAME_INFORMATION ObjectNameInfo
= (POBJECT_NAME_INFORMATION
)&Buffer
;
152 POBJECT_NAME_INFORMATION PoolObjectNameInfo
= NULL
;
153 ULONG ReturnedLength
, MessageLength
;
155 ULONG ExtraStringLength
;
158 /* Connect to the port */
159 if (!IopConnectLogPort()) return;
161 /* Allocate the message */
162 Message
= ExAllocatePool(PagedPool
, IO_ERROR_LOG_MESSAGE_LENGTH
);
165 /* Couldn't allocate, try again */
166 IopRestartLogWorker();
170 /* Copy the message */
171 RtlZeroMemory(Message
, sizeof(ELF_API_MSG
));
173 /* Get the actual I/O Structure */
174 ErrorMessage
= &Message
->IoErrorMessage
;
180 ListEntry
= IopGetErrorLogEntry();
181 if (!ListEntry
) break;
182 LogEntry
= CONTAINING_RECORD(ListEntry
, ERROR_LOG_ENTRY
, ListEntry
);
184 /* Get pointer to the log packet */
185 Packet
= (PIO_ERROR_LOG_PACKET
)((ULONG_PTR
)LogEntry
+
186 sizeof(ERROR_LOG_ENTRY
));
188 /* Calculate the total length of the message only */
189 MessageLength
= sizeof(IO_ERROR_LOG_MESSAGE
) -
190 sizeof(ERROR_LOG_ENTRY
) -
191 sizeof(IO_ERROR_LOG_PACKET
) +
194 /* Copy the packet */
195 RtlCopyMemory(&ErrorMessage
->EntryData
,
197 LogEntry
->Size
- sizeof(ERROR_LOG_ENTRY
));
199 /* Set the timestamp and time */
200 ErrorMessage
->TimeStamp
= LogEntry
->TimeStamp
;
201 ErrorMessage
->Type
= IO_TYPE_ERROR_MESSAGE
;
203 /* Check if this message has any strings */
204 if (Packet
->NumberOfStrings
)
206 /* String buffer is after the current strings */
207 StringBuffer
= (PCHAR
)&ErrorMessage
->EntryData
+
208 Packet
->StringOffset
;
212 /* Otherwise, string buffer is at the end */
213 StringBuffer
= (PCHAR
)ErrorMessage
+ MessageLength
;
216 /* Align the buffer */
217 StringBuffer
= ALIGN_UP_POINTER(StringBuffer
, WCHAR
);
219 /* Set the offset for the driver's name to the current buffer */
220 ErrorMessage
->DriverNameOffset
= (ULONG
)(StringBuffer
-
221 (PCHAR
)ErrorMessage
);
223 /* Check how much space we have left for the device string */
224 RemainingLength
= (ULONG
)((ULONG_PTR
)Message
+
225 IO_ERROR_LOG_MESSAGE_LENGTH
-
226 (ULONG_PTR
)StringBuffer
);
228 /* Now check if there is a driver object */
229 DriverObject
= LogEntry
->DriverObject
;
232 /* Check if the driver has a name */
233 if (DriverObject
->DriverName
.Buffer
)
236 DriverNameString
.Buffer
= DriverObject
->DriverName
.Buffer
;
237 DriverNameLength
= DriverObject
->DriverName
.Length
;
240 DriverNameString
.Buffer
= NULL
;
242 /* Check if there isn't a valid name*/
243 if (!DriverNameLength
)
245 /* Query the name directly */
246 Status
= ObQueryNameString(DriverObject
,
250 if (!(NT_SUCCESS(Status
)) || !(ObjectNameInfo
->Name
.Length
))
252 /* We don't have a name */
253 DriverNameLength
= 0;
259 /* Use default name */
260 DriverNameString
.Buffer
= L
"Application Popup";
261 DriverNameLength
= (ULONG
)wcslen(DriverNameString
.Buffer
) * sizeof(WCHAR
);
264 /* Check if we have a driver name by here */
265 if (DriverNameLength
)
267 /* Skip to the end of the driver's name */
268 p
= &DriverNameString
.Buffer
[DriverNameLength
/ sizeof(WCHAR
)];
270 /* Now we'll walk backwards and assume the minimum size */
271 DriverNameLength
= sizeof(WCHAR
);
273 while ((*p
!= L
'\\') && (p
!= DriverNameString
.Buffer
))
275 /* No backslash found, keep going */
277 DriverNameLength
+= sizeof(WCHAR
);
280 /* Now we probably hit the backslash itself, skip past it */
284 DriverNameLength
-= sizeof(WCHAR
);
288 * Now make sure that the driver name fits in our buffer, minus 3
289 * NULL chars, and copy the name in our string buffer
291 DriverNameLength
= min(DriverNameLength
,
292 RemainingLength
- 3 * sizeof(UNICODE_NULL
));
293 RtlCopyMemory(StringBuffer
, p
, DriverNameLength
);
296 /* Null-terminate the driver name */
297 *((PWSTR
)(StringBuffer
+ DriverNameLength
)) = L
'\0';
298 DriverNameLength
+= sizeof(WCHAR
);
300 /* Go to the next string buffer position */
301 StringBuffer
+= DriverNameLength
;
302 RemainingLength
-= DriverNameLength
;
304 /* Update the string offset and check if we have a device object */
305 ErrorMessage
->EntryData
.StringOffset
= (USHORT
)
306 ((ULONG_PTR
)StringBuffer
-
307 (ULONG_PTR
)ErrorMessage
);
308 if (LogEntry
->DeviceObject
)
310 /* We do, query its name */
311 Status
= ObQueryNameString(LogEntry
->DeviceObject
,
313 sizeof(OBJECT_NAME_INFORMATION
) +
317 if ((!NT_SUCCESS(Status
)) || !(ObjectNameInfo
->Name
.Length
))
319 /* Setup an empty name */
320 ObjectNameInfo
->Name
.Length
= 0;
321 ObjectNameInfo
->Name
.Buffer
= L
"";
323 /* Check if we failed because our buffer wasn't large enough */
324 if (Status
== STATUS_INFO_LENGTH_MISMATCH
)
326 /* Then we'll allocate one... we really want this name! */
327 PoolObjectNameInfo
= ExAllocatePoolWithTag(PagedPool
,
330 if (PoolObjectNameInfo
)
333 ObjectNameInfo
= PoolObjectNameInfo
;
334 Status
= ObQueryNameString(LogEntry
->DeviceObject
,
338 if (NT_SUCCESS(Status
))
340 /* Success, update the information */
341 ObjectNameInfo
->Name
.Length
=
342 100 - (USHORT
)DriverNameLength
;
350 /* No device object, setup an empty name */
351 ObjectNameInfo
->Name
.Length
= 0;
352 ObjectNameInfo
->Name
.Buffer
= L
"";
356 * Now make sure that the device name fits in our buffer, minus 2
357 * NULL chars, and copy the name in our string buffer
359 DeviceNameLength
= min(ObjectNameInfo
->Name
.Length
,
360 RemainingLength
- 2 * sizeof(UNICODE_NULL
));
361 RtlCopyMemory(StringBuffer
,
362 ObjectNameInfo
->Name
.Buffer
,
365 /* Null-terminate the device name */
366 *((PWSTR
)(StringBuffer
+ DeviceNameLength
)) = L
'\0';
367 DeviceNameLength
+= sizeof(WCHAR
);
369 /* Free the buffer if we had one */
370 if (PoolObjectNameInfo
)
372 ExFreePool(PoolObjectNameInfo
);
373 PoolObjectNameInfo
= NULL
;
376 /* Go to the next string buffer position */
377 ErrorMessage
->EntryData
.NumberOfStrings
++;
378 StringBuffer
+= DeviceNameLength
;
379 RemainingLength
-= DeviceNameLength
;
381 /* Check if we have any extra strings */
382 if (Packet
->NumberOfStrings
)
384 /* Find out the size of the extra strings */
385 ExtraStringLength
= LogEntry
->Size
-
386 sizeof(ERROR_LOG_ENTRY
) -
387 Packet
->StringOffset
;
389 /* Make sure that the extra strings fit in our buffer */
390 if (ExtraStringLength
> (RemainingLength
- sizeof(UNICODE_NULL
)))
392 /* They wouldn't, so set normalize the length */
393 MessageLength
-= ExtraStringLength
- RemainingLength
;
394 ExtraStringLength
= RemainingLength
- sizeof(UNICODE_NULL
);
397 /* Now copy the extra strings */
398 RtlCopyMemory(StringBuffer
,
399 (PCHAR
)Packet
+ Packet
->StringOffset
,
402 /* Null-terminate them */
403 *((PWSTR
)(StringBuffer
+ ExtraStringLength
)) = L
'\0';
406 /* Set the driver name length */
407 ErrorMessage
->DriverNameLength
= (USHORT
)DriverNameLength
;
409 /* Update the message length to include the device and driver names */
410 MessageLength
+= DeviceNameLength
+ DriverNameLength
;
411 ErrorMessage
->Size
= (USHORT
)MessageLength
;
413 /* Now update it again, internally, for the size of the actual LPC */
414 MessageLength
+= (FIELD_OFFSET(ELF_API_MSG
, IoErrorMessage
) -
415 FIELD_OFFSET(ELF_API_MSG
, Unknown
[0]));
417 /* Set the total and data lengths */
418 Message
->h
.u1
.s1
.TotalLength
= (USHORT
)(sizeof(PORT_MESSAGE
) +
420 Message
->h
.u1
.s1
.DataLength
= (USHORT
)(MessageLength
);
422 /* Send the message */
423 Status
= NtRequestPort(IopLogPort
, (PPORT_MESSAGE
)Message
);
424 if (!NT_SUCCESS(Status
))
426 /* Requeue log message and restart the worker */
427 ExInterlockedInsertTailList(&IopErrorLogListHead
,
428 &LogEntry
->ListEntry
,
430 IopLogWorkerRunning
= FALSE
;
431 IopRestartLogWorker();
435 /* Dereference the device object */
436 if (LogEntry
->DeviceObject
) ObDereferenceObject(LogEntry
->DeviceObject
);
437 if (DriverObject
) ObDereferenceObject(LogEntry
->DriverObject
);
440 InterlockedExchangeAdd(&IopTotalLogSize
,
441 -(LONG
)(LogEntry
->Size
-
442 sizeof(ERROR_LOG_ENTRY
)));
445 /* Free the LPC Message */
451 IopFreeApc(IN PKAPC Apc
,
452 IN PKNORMAL_ROUTINE
*NormalRoutine
,
453 IN PVOID
*NormalContext
,
454 IN PVOID
*SystemArgument1
,
455 IN PVOID
*SystemArgument2
)
463 IopRaiseHardError(IN PKAPC Apc
,
464 IN PKNORMAL_ROUTINE
*NormalRoutine
,
465 IN PVOID
*NormalContext
,
466 IN PVOID
*SystemArgument1
,
467 IN PVOID
*SystemArgument2
)
469 PIRP Irp
= (PIRP
)NormalContext
;
470 //PVPB Vpb = (PVPB)SystemArgument1;
471 //PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument2;
475 /* FIXME: UNIMPLEMENTED */
476 Irp
->IoStatus
.Status
= STATUS_NOT_IMPLEMENTED
;
477 Irp
->IoStatus
.Information
= 0;
478 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
481 /* PUBLIC FUNCTIONS **********************************************************/
488 IoAllocateErrorLogEntry(IN PVOID IoObject
,
491 PERROR_LOG_ENTRY LogEntry
;
493 PDEVICE_OBJECT DeviceObject
;
494 PDRIVER_OBJECT DriverObject
;
496 /* Make sure we have an object */
497 if (!IoObject
) return NULL
;
499 /* Check if we're past our buffer */
500 if (IopTotalLogSize
> PAGE_SIZE
) return NULL
;
502 /* Check if this is a device object or driver object */
503 if (((PDEVICE_OBJECT
)IoObject
)->Type
== IO_TYPE_DEVICE
)
505 /* It's a device, get the driver */
506 DeviceObject
= (PDEVICE_OBJECT
)IoObject
;
507 DriverObject
= DeviceObject
->DriverObject
;
509 else if (((PDEVICE_OBJECT
)IoObject
)->Type
== IO_TYPE_DRIVER
)
511 /* It's a driver, so we don't have a device */
513 DriverObject
= (PDRIVER_OBJECT
)IoObject
;
521 /* Calculate the total size and allocate it */
522 LogEntrySize
= sizeof(ERROR_LOG_ENTRY
) + EntrySize
;
523 LogEntry
= ExAllocatePoolWithTag(NonPagedPool
,
526 if (!LogEntry
) return NULL
;
528 /* Reference the Objects */
529 if (DeviceObject
) ObReferenceObject(DeviceObject
);
530 if (DriverObject
) ObReferenceObject(DriverObject
);
532 /* Update log size */
533 InterlockedExchangeAdd(&IopTotalLogSize
, EntrySize
);
535 /* Clear the entry and set it up */
536 RtlZeroMemory(LogEntry
, EntrySize
);
537 LogEntry
->Type
= IO_TYPE_ERROR_LOG
;
538 LogEntry
->Size
= EntrySize
;
539 LogEntry
->DeviceObject
= DeviceObject
;
540 LogEntry
->DriverObject
= DriverObject
;
542 /* Return the entry data */
543 return (PVOID
)((ULONG_PTR
)LogEntry
+ sizeof(ERROR_LOG_ENTRY
));
551 IoFreeErrorLogEntry(IN PVOID ElEntry
)
553 PERROR_LOG_ENTRY LogEntry
;
555 /* Make sure there's an entry */
556 if (!ElEntry
) return;
558 /* Get the actual header */
559 LogEntry
= (PERROR_LOG_ENTRY
)((ULONG_PTR
)ElEntry
- sizeof(ERROR_LOG_ENTRY
));
561 /* Dereference both objects */
562 if (LogEntry
->DeviceObject
) ObDereferenceObject(LogEntry
->DeviceObject
);
563 if (LogEntry
->DriverObject
) ObDereferenceObject(LogEntry
->DriverObject
);
565 /* Decrease total allocation size and free the entry */
566 InterlockedExchangeAdd(&IopTotalLogSize
,
567 -(LONG
)(LogEntry
->Size
- sizeof(ERROR_LOG_ENTRY
)));
568 ExFreePool(LogEntry
);
576 IoWriteErrorLogEntry(IN PVOID ElEntry
)
578 PERROR_LOG_ENTRY LogEntry
;
581 /* Get the main header */
582 LogEntry
= (PERROR_LOG_ENTRY
)((ULONG_PTR
)ElEntry
-
583 sizeof(ERROR_LOG_ENTRY
));
586 KeQuerySystemTime(&LogEntry
->TimeStamp
);
588 /* Acquire the lock and insert this write in the list */
589 KeAcquireSpinLock(&IopLogListLock
, &Irql
);
590 InsertHeadList(&IopErrorLogListHead
, &LogEntry
->ListEntry
);
592 /* Check if the worker is running */
593 if (!IopLogWorkerRunning
)
596 /* It's not, initialize it and queue it */
597 ExInitializeWorkItem(&IopErrorLogWorkItem
,
599 &IopErrorLogWorkItem
);
600 ExQueueWorkItem(&IopErrorLogWorkItem
, DelayedWorkQueue
);
601 IopLogWorkerRunning
= TRUE
;
605 /* Release the lock and return */
606 KeReleaseSpinLock(&IopLogListLock
, Irql
);
614 IoRaiseHardError(IN PIRP Irp
,
616 IN PDEVICE_OBJECT RealDeviceObject
)
618 PETHREAD Thread
= (PETHREAD
)&Irp
->Tail
.Overlay
.Thread
;
621 /* Don't do anything if hard errors are disabled on the thread */
622 if (Thread
->HardErrorsAreDisabled
)
624 /* Complete the request */
625 Irp
->IoStatus
.Information
= 0;
626 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
631 ErrorApc
= ExAllocatePoolWithTag(NonPagedPool
,
634 KeInitializeApc(ErrorApc
,
638 (PKRUNDOWN_ROUTINE
)IopFreeApc
,
639 (PKNORMAL_ROUTINE
)IopRaiseHardError
,
643 /* Queue an APC to deal with the error (see osr documentation) */
644 KeInsertQueueApc(ErrorApc
, Vpb
, RealDeviceObject
, 0);
652 IoRaiseInformationalHardError(IN NTSTATUS ErrorStatus
,
653 IN PUNICODE_STRING String
,
665 IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled
)
667 PETHREAD Thread
= PsGetCurrentThread();
670 /* Get the current value */
671 OldMode
= !Thread
->HardErrorsAreDisabled
;
673 /* Set the new one and return the old */
674 Thread
->HardErrorsAreDisabled
= !HardErrorEnabled
;