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 static 488 VOID 489 NTAPI 490 IopFreeApc(IN PKAPC Apc, 491 IN OUT PKNORMAL_ROUTINE* NormalRoutine, 492 IN OUT PVOID* NormalContext, 493 IN OUT PVOID* SystemArgument1, 494 IN OUT PVOID* SystemArgument2) 495 { 496 PAGED_CODE(); 497 498 /* Free the APC */ 499 ExFreePoolWithTag(Apc, TAG_APC); 500 } 501 502 static 503 VOID 504 NTAPI 505 IopRaiseHardError(IN PVOID NormalContext, 506 IN PVOID SystemArgument1, 507 IN PVOID SystemArgument2) 508 { 509 PIRP Irp = NormalContext; 510 //PVPB Vpb = SystemArgument1; 511 //PDEVICE_OBJECT DeviceObject = SystemArgument2; 512 513 UNIMPLEMENTED; 514 515 /* FIXME: UNIMPLEMENTED */ 516 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; 517 Irp->IoStatus.Information = 0; 518 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 519 } 520 521 /* PUBLIC FUNCTIONS **********************************************************/ 522 523 /* 524 * @implemented 525 */ 526 PVOID 527 NTAPI 528 IoAllocateErrorLogEntry(IN PVOID IoObject, 529 IN UCHAR EntrySize) 530 { 531 PERROR_LOG_ENTRY LogEntry; 532 ULONG LogEntrySize; 533 PDEVICE_OBJECT DeviceObject; 534 PDRIVER_OBJECT DriverObject; 535 536 /* Make sure we have an object */ 537 if (!IoObject) return NULL; 538 539 /* Check if this is a device object or driver object */ 540 DeviceObject = (PDEVICE_OBJECT)IoObject; 541 if (DeviceObject->Type == IO_TYPE_DEVICE) 542 { 543 /* It's a device, get the driver */ 544 // DeviceObject = (PDEVICE_OBJECT)IoObject; 545 DriverObject = DeviceObject->DriverObject; 546 } 547 else if (DeviceObject->Type == IO_TYPE_DRIVER) 548 { 549 /* It's a driver, so we don't have a device */ 550 DeviceObject = NULL; 551 DriverObject = (PDRIVER_OBJECT)IoObject; 552 } 553 else 554 { 555 /* Fail */ 556 return NULL; 557 } 558 559 /* Check whether the size is too small or too large */ 560 if ((EntrySize < sizeof(IO_ERROR_LOG_PACKET)) || 561 (EntrySize > ERROR_LOG_MAXIMUM_SIZE)) 562 { 563 /* Fail */ 564 return NULL; 565 } 566 567 /* Round up the size and calculate the total size */ 568 EntrySize = ROUND_UP(EntrySize, sizeof(PVOID)); 569 LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize; 570 571 /* Check if we're past our buffer */ 572 // TODO: Improve (what happens in case of concurrent calls?) 573 if (IopTotalLogSize + LogEntrySize > IOP_MAXIMUM_LOG_SIZE) return NULL; 574 575 /* Allocate the entry */ 576 LogEntry = ExAllocatePoolWithTag(NonPagedPool, 577 LogEntrySize, 578 TAG_ERROR_LOG); 579 if (!LogEntry) return NULL; 580 581 /* Reference the Objects */ 582 if (DeviceObject) ObReferenceObject(DeviceObject); 583 if (DriverObject) ObReferenceObject(DriverObject); 584 585 /* Update log size */ 586 InterlockedExchangeAdd(&IopTotalLogSize, LogEntrySize); 587 588 /* Clear the entry and set it up */ 589 RtlZeroMemory(LogEntry, LogEntrySize); 590 LogEntry->Type = IO_TYPE_ERROR_LOG; 591 LogEntry->Size = LogEntrySize; 592 LogEntry->DeviceObject = DeviceObject; 593 LogEntry->DriverObject = DriverObject; 594 595 /* Return the entry data */ 596 return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY)); 597 } 598 599 /* 600 * @implemented 601 */ 602 VOID 603 NTAPI 604 IoFreeErrorLogEntry(IN PVOID ElEntry) 605 { 606 PERROR_LOG_ENTRY LogEntry; 607 608 /* Make sure there is an entry */ 609 if (!ElEntry) return; 610 611 /* Get the actual header */ 612 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY)); 613 614 /* Dereference both objects */ 615 if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject); 616 if (LogEntry->DriverObject) ObDereferenceObject(LogEntry->DriverObject); 617 618 /* Decrease the total allocation size and free the entry */ 619 InterlockedExchangeAdd(&IopTotalLogSize, -(LONG)LogEntry->Size); 620 ExFreePoolWithTag(LogEntry, TAG_ERROR_LOG); 621 } 622 623 /* 624 * @implemented 625 */ 626 VOID 627 NTAPI 628 IoWriteErrorLogEntry(IN PVOID ElEntry) 629 { 630 PERROR_LOG_ENTRY LogEntry; 631 KIRQL Irql; 632 633 /* Make sure there is an entry */ 634 if (!ElEntry) return; 635 636 /* Get the actual header */ 637 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY)); 638 639 /* Get time stamp */ 640 KeQuerySystemTime(&LogEntry->TimeStamp); 641 642 /* Acquire the lock and insert this write in the list */ 643 KeAcquireSpinLock(&IopLogListLock, &Irql); 644 InsertHeadList(&IopErrorLogListHead, &LogEntry->ListEntry); 645 646 /* Check if the worker is running */ 647 if (!IopLogWorkerRunning) 648 { 649 /* It's not, initialize it and queue it */ 650 IopLogWorkerRunning = TRUE; 651 ExInitializeWorkItem(&IopErrorLogWorkItem, IopLogWorker, NULL); 652 ExQueueWorkItem(&IopErrorLogWorkItem, DelayedWorkQueue); 653 } 654 655 /* Release the lock and return */ 656 KeReleaseSpinLock(&IopLogListLock, Irql); 657 } 658 659 /* 660 * @implemented 661 */ 662 VOID 663 NTAPI 664 IoRaiseHardError(IN PIRP Irp, 665 IN PVPB Vpb, 666 IN PDEVICE_OBJECT RealDeviceObject) 667 { 668 PETHREAD Thread = Irp->Tail.Overlay.Thread; 669 PKAPC ErrorApc; 670 671 /* Don't do anything if hard errors are disabled on the thread */ 672 if (Thread->HardErrorsAreDisabled) 673 { 674 /* Complete the request */ 675 Irp->IoStatus.Information = 0; 676 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 677 return; 678 } 679 680 // TODO: In case we were called in the context of a paging I/O or for 681 // a synchronous operation, that happens with APCs disabled, queue the 682 // hard-error call for later processing (see also IofCompleteRequest). 683 684 /* Setup an APC and queue it to deal with the error (see OSR documentation) */ 685 ErrorApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ErrorApc), TAG_APC); 686 if (!ErrorApc) 687 { 688 /* Fail */ 689 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 690 return; 691 } 692 693 KeInitializeApc(ErrorApc, 694 &Thread->Tcb, 695 Irp->ApcEnvironment, 696 IopFreeApc, 697 NULL, 698 IopRaiseHardError, 699 KernelMode, 700 Irp); 701 702 KeInsertQueueApc(ErrorApc, Vpb, RealDeviceObject, IO_NO_INCREMENT); 703 } 704 705 /* 706 * @unimplemented 707 */ 708 BOOLEAN 709 NTAPI 710 IoRaiseInformationalHardError(IN NTSTATUS ErrorStatus, 711 IN PUNICODE_STRING String, 712 IN PKTHREAD Thread) 713 { 714 DPRINT1("IoRaiseInformationalHardError: %lx, '%wZ'\n", ErrorStatus, String); 715 return FALSE; 716 } 717 718 /* 719 * @implemented 720 */ 721 BOOLEAN 722 NTAPI 723 IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled) 724 { 725 PETHREAD Thread = PsGetCurrentThread(); 726 BOOLEAN OldMode; 727 728 /* Get the current value */ 729 OldMode = !Thread->HardErrorsAreDisabled; 730 731 /* Set the new one and return the old */ 732 Thread->HardErrorsAreDisabled = !HardErrorEnabled; 733 return OldMode; 734 } 735