dea65698c9ab86f3e514b2da470c99cb94fafeec
[reactos.git] / reactos / ntoskrnl / io / iomgr / error.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/error.c
5 * PURPOSE: I/O Error Functions and Error Log Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Eric Kohl
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #include <subsys/iolog/iolog.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* TYPES *********************************************************************/
19
20 typedef struct _IOP_ERROR_LOG_WORKER_DPC
21 {
22 KDPC Dpc;
23 KTIMER Timer;
24 } IOP_ERROR_LOG_WORKER_DPC, *PIOP_ERROR_LOG_WORKER_DPC;
25
26 /* GLOBALS *******************************************************************/
27
28 #define IOP_MAXIMUM_LOG_SIZE (100 * PAGE_SIZE)
29 LONG IopTotalLogSize;
30 LIST_ENTRY IopErrorLogListHead;
31 KSPIN_LOCK IopLogListLock;
32
33 BOOLEAN IopLogWorkerRunning;
34 BOOLEAN IopLogPortConnected;
35 HANDLE IopLogPort;
36 WORK_QUEUE_ITEM IopErrorLogWorkItem;
37
38 PDEVICE_OBJECT IopErrorLogObject;
39
40 /* PRIVATE FUNCTIONS *********************************************************/
41
42 VOID
43 NTAPI
44 IopLogDpcRoutine(IN PKDPC Dpc,
45 IN PVOID DeferredContext,
46 IN PVOID SystemArgument1,
47 IN PVOID SystemArgument2)
48 {
49 /* If we have a DPC, free it */
50 if (Dpc) ExFreePool(Dpc);
51
52 /* Initialize and queue the work item */
53 ExInitializeWorkItem(&IopErrorLogWorkItem, IopLogWorker, NULL);
54 ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue);
55 }
56
57 PLIST_ENTRY
58 NTAPI
59 IopGetErrorLogEntry(VOID)
60 {
61 KIRQL OldIrql;
62 PLIST_ENTRY ListEntry;
63
64 /* Acquire the lock and check if the list is empty */
65 KeAcquireSpinLock(&IopLogListLock, &OldIrql);
66 if (IsListEmpty(&IopErrorLogListHead))
67 {
68 /* List is empty, disable the worker and return NULL */
69 IopLogWorkerRunning = FALSE;
70 ListEntry = NULL;
71 }
72 else
73 {
74 /* Otherwise, remove an entry */
75 ListEntry = RemoveHeadList(&IopErrorLogListHead);
76 }
77
78 /* Release the lock and return the entry */
79 KeReleaseSpinLock(&IopLogListLock, OldIrql);
80 return ListEntry;
81 }
82
83 VOID
84 NTAPI
85 IopRestartLogWorker(VOID)
86 {
87 PIOP_ERROR_LOG_WORKER_DPC WorkerDpc;
88 LARGE_INTEGER Timeout;
89
90 /* Allocate a DPC Context */
91 WorkerDpc = ExAllocatePool(NonPagedPool, sizeof(IOP_ERROR_LOG_WORKER_DPC));
92 if (!WorkerDpc)
93 {
94 /* Fail */
95 IopLogWorkerRunning = FALSE;
96 return;
97 }
98
99 /* Initialize DPC and Timer */
100 KeInitializeDpc(&WorkerDpc->Dpc, IopLogDpcRoutine, WorkerDpc);
101 KeInitializeTimer(&WorkerDpc->Timer);
102
103 /* Restart after 30 seconds */
104 Timeout.QuadPart = (LONGLONG)-300000000;
105 KeSetTimer(&WorkerDpc->Timer, Timeout, &WorkerDpc->Dpc);
106 }
107
108 BOOLEAN
109 NTAPI
110 IopConnectLogPort(VOID)
111 {
112 NTSTATUS Status;
113 UNICODE_STRING PortName = RTL_CONSTANT_STRING(ELF_PORT_NAME);
114 SECURITY_QUALITY_OF_SERVICE SecurityQos;
115
116 /* Make sure we're not already connected */
117 if (IopLogPortConnected) return TRUE;
118
119 /* Setup the QoS structure */
120 SecurityQos.Length = sizeof(SecurityQos);
121 SecurityQos.ImpersonationLevel = SecurityIdentification;
122 SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
123 SecurityQos.EffectiveOnly = TRUE;
124
125 /* Connect the port */
126 Status = ZwConnectPort(&IopLogPort,
127 &PortName,
128 &SecurityQos,
129 NULL,
130 NULL,
131 NULL,
132 NULL,
133 NULL);
134 if (NT_SUCCESS(Status))
135 {
136 /* Remember we're connected */
137 IopLogPortConnected = TRUE;
138 return TRUE;
139 }
140
141 /* We failed, try again */
142 IopRestartLogWorker();
143 return FALSE;
144 }
145
146 VOID
147 NTAPI
148 IopLogWorker(IN PVOID Parameter)
149 {
150 #define IO_ERROR_OBJECT_NAMES_LENGTH 100
151
152 NTSTATUS Status;
153 PELF_API_MSG Message;
154 PIO_ERROR_LOG_MESSAGE ErrorMessage;
155 PLIST_ENTRY ListEntry;
156 PERROR_LOG_ENTRY LogEntry;
157 PIO_ERROR_LOG_PACKET Packet;
158 PCHAR StringBuffer;
159 ULONG RemainingLength;
160 PDRIVER_OBJECT DriverObject;
161 PWCHAR NameString;
162 ULONG DriverNameLength, DeviceNameLength;
163 UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + IO_ERROR_OBJECT_NAMES_LENGTH];
164 POBJECT_NAME_INFORMATION ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer;
165 POBJECT_NAME_INFORMATION PoolObjectNameInfo;
166 ULONG ReturnedLength, MessageLength;
167 ULONG ExtraStringLength;
168 PWCHAR p;
169
170 PAGED_CODE();
171
172 UNREFERENCED_PARAMETER(Parameter);
173
174 /* Connect to the port */
175 if (!IopConnectLogPort()) return;
176
177 /* Allocate the message */
178 Message = ExAllocatePoolWithTag(PagedPool, IO_ERROR_LOG_MESSAGE_LENGTH, TAG_IO);
179 if (!Message)
180 {
181 /* Couldn't allocate, try again */
182 IopRestartLogWorker();
183 return;
184 }
185
186 /* Zero out the message and get the actual I/O structure */
187 RtlZeroMemory(Message, sizeof(*Message));
188 ErrorMessage = &Message->IoErrorMessage;
189
190 /* Start loop */
191 while (TRUE)
192 {
193 /* Get an entry */
194 ListEntry = IopGetErrorLogEntry();
195 if (!ListEntry) break;
196 LogEntry = CONTAINING_RECORD(ListEntry, ERROR_LOG_ENTRY, ListEntry);
197
198 /* Get pointer to the log packet */
199 Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry +
200 sizeof(ERROR_LOG_ENTRY));
201
202 /* Calculate the total length of the message only */
203 MessageLength = sizeof(IO_ERROR_LOG_MESSAGE) -
204 sizeof(ERROR_LOG_ENTRY) -
205 sizeof(IO_ERROR_LOG_PACKET) +
206 LogEntry->Size;
207
208 /* Copy the packet */
209 RtlCopyMemory(&ErrorMessage->EntryData,
210 Packet,
211 LogEntry->Size - sizeof(ERROR_LOG_ENTRY));
212
213 /* Set the timestamp and time */
214 ErrorMessage->TimeStamp = LogEntry->TimeStamp;
215 ErrorMessage->Type = IO_TYPE_ERROR_MESSAGE;
216
217 /* Check if this message has any strings */
218 if (Packet->NumberOfStrings)
219 {
220 /* String buffer is after the current strings */
221 StringBuffer = (PCHAR)&ErrorMessage->EntryData +
222 Packet->StringOffset;
223 }
224 else
225 {
226 /* Otherwise, string buffer is at the end */
227 StringBuffer = (PCHAR)ErrorMessage + MessageLength;
228 }
229
230 /* Align the buffer */
231 StringBuffer = ALIGN_UP_POINTER(StringBuffer, WCHAR);
232
233 /* Set the offset for the driver's name to the current buffer */
234 ErrorMessage->DriverNameOffset = (ULONG)(StringBuffer -
235 (PCHAR)ErrorMessage);
236
237 /* Check how much space we have left for the device string */
238 RemainingLength = (ULONG)((ULONG_PTR)Message +
239 IO_ERROR_LOG_MESSAGE_LENGTH -
240 (ULONG_PTR)StringBuffer);
241
242 NameString = NULL;
243 DriverNameLength = 0; DeviceNameLength = 0;
244 PoolObjectNameInfo = NULL;
245 ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer;
246
247 /* Now check if there is a driver object */
248 DriverObject = LogEntry->DriverObject;
249 if (DriverObject)
250 {
251 /* Check if the driver has a name, and use it if so */
252 if (DriverObject->DriverName.Buffer)
253 {
254 NameString = DriverObject->DriverName.Buffer;
255 DriverNameLength = DriverObject->DriverName.Length;
256 }
257 else
258 {
259 NameString = NULL;
260 DriverNameLength = 0;
261 }
262
263 /* Check if there isn't a valid name */
264 if (!DriverNameLength)
265 {
266 /* Query the name directly */
267 Status = ObQueryNameString(DriverObject,
268 ObjectNameInfo,
269 sizeof(Buffer),
270 &ReturnedLength);
271 if (!NT_SUCCESS(Status) || (ObjectNameInfo->Name.Length == 0))
272 {
273 /* We don't have a name */
274 DriverNameLength = 0;
275 }
276 else
277 {
278 NameString = ObjectNameInfo->Name.Buffer;
279 DriverNameLength = ObjectNameInfo->Name.Length;
280 }
281 }
282 }
283 else
284 {
285 /* Use default name */
286 NameString = L"Application Popup";
287 DriverNameLength = (ULONG)wcslen(NameString) * sizeof(WCHAR);
288 }
289
290 /* Check if we have a driver name */
291 if (DriverNameLength)
292 {
293 /* Skip to the end of the driver's name */
294 p = &NameString[DriverNameLength / sizeof(WCHAR)];
295
296 /* Now we'll walk backwards and assume the minimum size */
297 DriverNameLength = sizeof(WCHAR);
298 p--;
299 while ((*p != L'\\') && (p != NameString))
300 {
301 /* No backslash found, keep going */
302 p--;
303 DriverNameLength += sizeof(WCHAR);
304 }
305
306 /* Now we probably hit the backslash itself, skip past it */
307 if (*p == L'\\')
308 {
309 p++;
310 DriverNameLength -= sizeof(WCHAR);
311 }
312
313 /*
314 * Now make sure that the driver name fits in the buffer, minus
315 * 3 NULL chars (driver name, device name, and remaining strings),
316 * and copy the driver name in the string buffer.
317 */
318 DriverNameLength = min(DriverNameLength,
319 RemainingLength - 3 * sizeof(UNICODE_NULL));
320 RtlCopyMemory(StringBuffer, p, DriverNameLength);
321 }
322
323 /* Null-terminate the driver name */
324 *((PWSTR)(StringBuffer + DriverNameLength)) = UNICODE_NULL;
325 DriverNameLength += sizeof(WCHAR);
326
327 /* Go to the next string buffer position */
328 StringBuffer += DriverNameLength;
329 RemainingLength -= DriverNameLength;
330
331 /* Update the string offset */
332 ErrorMessage->EntryData.StringOffset =
333 (USHORT)((ULONG_PTR)StringBuffer - (ULONG_PTR)ErrorMessage);
334
335 /* Check if we have a device object */
336 if (LogEntry->DeviceObject)
337 {
338 /* We do, query its name */
339 Status = ObQueryNameString(LogEntry->DeviceObject,
340 ObjectNameInfo,
341 sizeof(Buffer) - DriverNameLength,
342 &ReturnedLength);
343 if (!NT_SUCCESS(Status) || (ObjectNameInfo->Name.Length == 0))
344 {
345 /* Setup an empty name */
346 RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, L"", 0);
347
348 /* Check if we failed because our buffer wasn't large enough */
349 if (Status == STATUS_INFO_LENGTH_MISMATCH)
350 {
351 /* Then we'll allocate one... we really want this name! */
352 PoolObjectNameInfo = ExAllocatePoolWithTag(PagedPool,
353 ReturnedLength,
354 TAG_IO);
355 if (PoolObjectNameInfo)
356 {
357 /* Query it again */
358 ObjectNameInfo = PoolObjectNameInfo;
359 Status = ObQueryNameString(LogEntry->DeviceObject,
360 ObjectNameInfo,
361 ReturnedLength,
362 &ReturnedLength);
363 if (NT_SUCCESS(Status))
364 {
365 /* Success, update the information */
366 ObjectNameInfo->Name.Length =
367 IO_ERROR_OBJECT_NAMES_LENGTH - (USHORT)DriverNameLength;
368 }
369 }
370 }
371 }
372
373 NameString = ObjectNameInfo->Name.Buffer;
374 DeviceNameLength = ObjectNameInfo->Name.Length;
375 }
376 else
377 {
378 /* No device object, setup an empty name */
379 NameString = L"";
380 DeviceNameLength = 0;
381 }
382
383 /*
384 * Now make sure that the device name fits in the buffer, minus
385 * 2 NULL chars (device name, and remaining strings), and copy
386 * the device name in the string buffer.
387 */
388 DeviceNameLength = min(DeviceNameLength,
389 RemainingLength - 2 * sizeof(UNICODE_NULL));
390 RtlCopyMemory(StringBuffer, NameString, DeviceNameLength);
391
392 /* Null-terminate the device name */
393 *((PWSTR)(StringBuffer + DeviceNameLength)) = UNICODE_NULL;
394 DeviceNameLength += sizeof(WCHAR);
395
396 /* Free the buffer if we had one */
397 if (PoolObjectNameInfo)
398 {
399 ExFreePoolWithTag(PoolObjectNameInfo, TAG_IO);
400 PoolObjectNameInfo = NULL;
401 }
402
403 /* Go to the next string buffer position */
404 ErrorMessage->EntryData.NumberOfStrings++;
405 StringBuffer += DeviceNameLength;
406 RemainingLength -= DeviceNameLength;
407
408 /* Check if we have any extra strings */
409 if (Packet->NumberOfStrings)
410 {
411 /* Find out the size of the extra strings */
412 ExtraStringLength = LogEntry->Size -
413 sizeof(ERROR_LOG_ENTRY) -
414 Packet->StringOffset;
415
416 /* Round up the length */
417 ExtraStringLength = ROUND_UP(ExtraStringLength, sizeof(WCHAR));
418
419 /* Make sure that the extra strings fit in our buffer */
420 if (ExtraStringLength > (RemainingLength - sizeof(UNICODE_NULL)))
421 {
422 /* They wouldn't, so set normalize the length */
423 MessageLength -= ExtraStringLength - RemainingLength;
424 ExtraStringLength = RemainingLength - sizeof(UNICODE_NULL);
425 }
426
427 /* Now copy the extra strings */
428 RtlCopyMemory(StringBuffer,
429 (PCHAR)Packet + Packet->StringOffset,
430 ExtraStringLength);
431
432 /* Null-terminate them */
433 *((PWSTR)(StringBuffer + ExtraStringLength)) = UNICODE_NULL;
434 }
435
436 /* Set the driver name length */
437 ErrorMessage->DriverNameLength = (USHORT)DriverNameLength;
438
439 /* Update the message length to include the driver and device names */
440 MessageLength += DriverNameLength + DeviceNameLength;
441 ErrorMessage->Size = (USHORT)MessageLength;
442
443 /* Now update it again for the size of the actual LPC */
444 MessageLength += (FIELD_OFFSET(ELF_API_MSG, IoErrorMessage) -
445 FIELD_OFFSET(ELF_API_MSG, Unknown[0]));
446
447 /* Set the total and data lengths */
448 Message->Header.u1.s1.TotalLength =
449 (USHORT)(sizeof(PORT_MESSAGE) + MessageLength);
450 Message->Header.u1.s1.DataLength = (USHORT)MessageLength;
451
452 /* Send the message */
453 Status = ZwRequestPort(IopLogPort, &Message->Header);
454 if (!NT_SUCCESS(Status))
455 {
456 /*
457 * An error happened while sending the message on the port.
458 * Close the port, requeue the log message on top of the list
459 * and restart the worker.
460 */
461 ZwClose(IopLogPort);
462 IopLogPortConnected = FALSE;
463
464 ExInterlockedInsertHeadList(&IopErrorLogListHead,
465 &LogEntry->ListEntry,
466 &IopLogListLock);
467
468 IopRestartLogWorker();
469 break;
470 }
471
472 /* NOTE: The following is basically 'IoFreeErrorLogEntry(Packet)' */
473
474 /* Dereference both objects */
475 if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject);
476 if (LogEntry->DriverObject) ObDereferenceObject(LogEntry->DriverObject);
477
478 /* Decrease the total allocation size and free the entry */
479 InterlockedExchangeAdd(&IopTotalLogSize, -(LONG)LogEntry->Size);
480 ExFreePoolWithTag(LogEntry, TAG_ERROR_LOG);
481 }
482
483 /* Free the LPC Message */
484 ExFreePoolWithTag(Message, TAG_IO);
485 }
486
487 VOID
488 NTAPI
489 IopFreeApc(IN PKAPC Apc,
490 IN PKNORMAL_ROUTINE *NormalRoutine,
491 IN PVOID *NormalContext,
492 IN PVOID *SystemArgument1,
493 IN PVOID *SystemArgument2)
494 {
495 /* Free the APC */
496 ExFreePool(Apc);
497 }
498
499 VOID
500 NTAPI
501 IopRaiseHardError(IN PKAPC Apc,
502 IN PKNORMAL_ROUTINE *NormalRoutine,
503 IN PVOID *NormalContext,
504 IN PVOID *SystemArgument1,
505 IN PVOID *SystemArgument2)
506 {
507 PIRP Irp = (PIRP)NormalContext;
508 //PVPB Vpb = (PVPB)SystemArgument1;
509 //PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument2;
510
511 UNIMPLEMENTED;
512
513 /* FIXME: UNIMPLEMENTED */
514 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
515 Irp->IoStatus.Information = 0;
516 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
517 }
518
519 /* PUBLIC FUNCTIONS **********************************************************/
520
521 /*
522 * @implemented
523 */
524 PVOID
525 NTAPI
526 IoAllocateErrorLogEntry(IN PVOID IoObject,
527 IN UCHAR EntrySize)
528 {
529 PERROR_LOG_ENTRY LogEntry;
530 ULONG LogEntrySize;
531 PDEVICE_OBJECT DeviceObject;
532 PDRIVER_OBJECT DriverObject;
533
534 /* Make sure we have an object */
535 if (!IoObject) return NULL;
536
537 /* Check if this is a device object or driver object */
538 DeviceObject = (PDEVICE_OBJECT)IoObject;
539 if (DeviceObject->Type == IO_TYPE_DEVICE)
540 {
541 /* It's a device, get the driver */
542 // DeviceObject = (PDEVICE_OBJECT)IoObject;
543 DriverObject = DeviceObject->DriverObject;
544 }
545 else if (DeviceObject->Type == IO_TYPE_DRIVER)
546 {
547 /* It's a driver, so we don't have a device */
548 DeviceObject = NULL;
549 DriverObject = (PDRIVER_OBJECT)IoObject;
550 }
551 else
552 {
553 /* Fail */
554 return NULL;
555 }
556
557 /* Check whether the size is too small or too large */
558 if ((EntrySize < sizeof(IO_ERROR_LOG_PACKET)) ||
559 (EntrySize > ERROR_LOG_MAXIMUM_SIZE))
560 {
561 /* Fail */
562 return NULL;
563 }
564
565 /* Round up the size and calculate the total size */
566 EntrySize = ROUND_UP(EntrySize, sizeof(PVOID));
567 LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize;
568
569 /* Check if we're past our buffer */
570 // TODO: Improve (what happens in case of concurrent calls?)
571 if (IopTotalLogSize + LogEntrySize > IOP_MAXIMUM_LOG_SIZE) return NULL;
572
573 /* Allocate the entry */
574 LogEntry = ExAllocatePoolWithTag(NonPagedPool,
575 LogEntrySize,
576 TAG_ERROR_LOG);
577 if (!LogEntry) return NULL;
578
579 /* Reference the Objects */
580 if (DeviceObject) ObReferenceObject(DeviceObject);
581 if (DriverObject) ObReferenceObject(DriverObject);
582
583 /* Update log size */
584 InterlockedExchangeAdd(&IopTotalLogSize, LogEntrySize);
585
586 /* Clear the entry and set it up */
587 RtlZeroMemory(LogEntry, LogEntrySize);
588 LogEntry->Type = IO_TYPE_ERROR_LOG;
589 LogEntry->Size = LogEntrySize;
590 LogEntry->DeviceObject = DeviceObject;
591 LogEntry->DriverObject = DriverObject;
592
593 /* Return the entry data */
594 return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
595 }
596
597 /*
598 * @implemented
599 */
600 VOID
601 NTAPI
602 IoFreeErrorLogEntry(IN PVOID ElEntry)
603 {
604 PERROR_LOG_ENTRY LogEntry;
605
606 /* Make sure there is an entry */
607 if (!ElEntry) return;
608
609 /* Get the actual header */
610 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
611
612 /* Dereference both objects */
613 if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject);
614 if (LogEntry->DriverObject) ObDereferenceObject(LogEntry->DriverObject);
615
616 /* Decrease the total allocation size and free the entry */
617 InterlockedExchangeAdd(&IopTotalLogSize, -(LONG)LogEntry->Size);
618 ExFreePoolWithTag(LogEntry, TAG_ERROR_LOG);
619 }
620
621 /*
622 * @implemented
623 */
624 VOID
625 NTAPI
626 IoWriteErrorLogEntry(IN PVOID ElEntry)
627 {
628 PERROR_LOG_ENTRY LogEntry;
629 KIRQL Irql;
630
631 /* Make sure there is an entry */
632 if (!ElEntry) return;
633
634 /* Get the actual header */
635 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
636
637 /* Get time stamp */
638 KeQuerySystemTime(&LogEntry->TimeStamp);
639
640 /* Acquire the lock and insert this write in the list */
641 KeAcquireSpinLock(&IopLogListLock, &Irql);
642 InsertHeadList(&IopErrorLogListHead, &LogEntry->ListEntry);
643
644 /* Check if the worker is running */
645 if (!IopLogWorkerRunning)
646 {
647 /* It's not, initialize it and queue it */
648 IopLogWorkerRunning = TRUE;
649 ExInitializeWorkItem(&IopErrorLogWorkItem, IopLogWorker, NULL);
650 ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue);
651 }
652
653 /* Release the lock and return */
654 KeReleaseSpinLock(&IopLogListLock, Irql);
655 }
656
657 /*
658 * @implemented
659 */
660 VOID
661 NTAPI
662 IoRaiseHardError(IN PIRP Irp,
663 IN PVPB Vpb,
664 IN PDEVICE_OBJECT RealDeviceObject)
665 {
666 PETHREAD Thread = (PETHREAD)&Irp->Tail.Overlay.Thread;
667 PKAPC ErrorApc;
668
669 /* Don't do anything if hard errors are disabled on the thread */
670 if (Thread->HardErrorsAreDisabled)
671 {
672 /* Complete the request */
673 Irp->IoStatus.Information = 0;
674 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
675 return;
676 }
677
678 /* Setup an APC */
679 ErrorApc = ExAllocatePoolWithTag(NonPagedPool,
680 sizeof(KAPC),
681 TAG_APC);
682 KeInitializeApc(ErrorApc,
683 &Thread->Tcb,
684 Irp->ApcEnvironment,
685 NULL,
686 (PKRUNDOWN_ROUTINE)IopFreeApc,
687 (PKNORMAL_ROUTINE)IopRaiseHardError,
688 KernelMode,
689 Irp);
690
691 /* Queue an APC to deal with the error (see osr documentation) */
692 KeInsertQueueApc(ErrorApc, Vpb, RealDeviceObject, 0);
693 }
694
695 /*
696 * @unimplemented
697 */
698 BOOLEAN
699 NTAPI
700 IoRaiseInformationalHardError(IN NTSTATUS ErrorStatus,
701 IN PUNICODE_STRING String,
702 IN PKTHREAD Thread)
703 {
704 UNIMPLEMENTED;
705 return FALSE;
706 }
707
708 /*
709 * @implemented
710 */
711 BOOLEAN
712 NTAPI
713 IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled)
714 {
715 PETHREAD Thread = PsGetCurrentThread();
716 BOOLEAN OldMode;
717
718 /* Get the current value */
719 OldMode = !Thread->HardErrorsAreDisabled;
720
721 /* Set the new one and return the old */
722 Thread->HardErrorsAreDisabled = !HardErrorEnabled;
723 return OldMode;
724 }