Cleanup isn't necessary after calling the driver in NtQueryDirectoryFile.
[reactos.git] / reactos / ntoskrnl / io / error.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/io/errlog.c
6 * PURPOSE: Error logging
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* TYPES *********************************************************************/
18
19 typedef struct _ERROR_LOG_ENTRY
20 {
21 LIST_ENTRY Entry;
22 LARGE_INTEGER TimeStamp;
23 PVOID IoObject;
24 ULONG PacketSize;
25 } ERROR_LOG_ENTRY, *PERROR_LOG_ENTRY;
26
27 typedef struct _LOG_WORKER_DPC
28 {
29 KDPC Dpc;
30 KTIMER Timer;
31 } LOG_WORKER_DPC, *PLOG_WORKER_DPC;
32
33
34 static VOID STDCALL
35 IopLogWorker (PVOID Parameter);
36
37
38 /* GLOBALS *******************************************************************/
39
40 static KSPIN_LOCK IopAllocationLock;
41 static ULONG IopTotalLogSize;
42
43 static KSPIN_LOCK IopLogListLock;
44 static LIST_ENTRY IopLogListHead;
45
46 static BOOLEAN IopLogWorkerRunning = FALSE;
47 static BOOLEAN IopLogPortConnected = FALSE;
48 static HANDLE IopLogPort;
49
50
51 /* FUNCTIONS *****************************************************************/
52
53 NTSTATUS
54 IopInitErrorLog (VOID)
55 {
56 IopTotalLogSize = 0;
57 KeInitializeSpinLock (&IopAllocationLock);
58
59 KeInitializeSpinLock (&IopLogListLock);
60 InitializeListHead (&IopLogListHead);
61
62 return STATUS_SUCCESS;
63 }
64
65
66 static VOID STDCALL
67 IopLogDpcRoutine (PKDPC Dpc,
68 PVOID DeferredContext,
69 PVOID SystemArgument1,
70 PVOID SystemArgument2)
71 {
72 PWORK_QUEUE_ITEM LogWorkItem;
73
74 DPRINT ("\nIopLogDpcRoutine() called\n");
75
76 /* Release the WorkerDpc struct */
77 ExFreePool (DeferredContext);
78
79 /* Allocate, initialize and restart a work item */
80 LogWorkItem = ExAllocatePool (NonPagedPool,
81 sizeof(WORK_QUEUE_ITEM));
82 if (LogWorkItem == NULL)
83 {
84 IopLogWorkerRunning = FALSE;
85 return;
86 }
87
88 ExInitializeWorkItem (LogWorkItem,
89 IopLogWorker,
90 LogWorkItem);
91
92 ExQueueWorkItem (LogWorkItem,
93 DelayedWorkQueue);
94 }
95
96
97 static VOID
98 IopRestartLogWorker (VOID)
99 {
100 PLOG_WORKER_DPC WorkerDpc;
101 LARGE_INTEGER Timeout;
102
103 DPRINT ("IopRestartWorker() called\n");
104
105 WorkerDpc = ExAllocatePool (NonPagedPool,
106 sizeof(LOG_WORKER_DPC));
107 if (WorkerDpc == NULL)
108 {
109 IopLogWorkerRunning = FALSE;
110 return;
111 }
112
113 /* Initialize DPC and Timer */
114 KeInitializeDpc (&WorkerDpc->Dpc,
115 IopLogDpcRoutine,
116 WorkerDpc);
117 KeInitializeTimer (&WorkerDpc->Timer);
118
119 /* Restart after 30 seconds */
120 Timeout.QuadPart = (LONGLONG)-300000000;
121 KeSetTimer (&WorkerDpc->Timer,
122 Timeout,
123 &WorkerDpc->Dpc);
124 }
125
126
127 static BOOLEAN
128 IopConnectLogPort (VOID)
129 {
130 UNICODE_STRING PortName;
131 NTSTATUS Status;
132
133 DPRINT ("IopConnectLogPort() called\n");
134
135 RtlInitUnicodeString (&PortName,
136 L"\\ErrorLogPort");
137
138 Status = ZwConnectPort (&IopLogPort,
139 &PortName,
140 NULL,
141 NULL,
142 NULL,
143 NULL,
144 NULL,
145 NULL);
146 if (!NT_SUCCESS(Status))
147 {
148 DPRINT ("ZwConnectPort() failed (Status %lx)\n", Status);
149 return FALSE;
150 }
151
152 DPRINT ("IopConnectLogPort() done\n");
153
154 return TRUE;
155 }
156
157
158 static VOID STDCALL
159 IopLogWorker (PVOID Parameter)
160 {
161 PERROR_LOG_ENTRY LogEntry;
162 PPORT_MESSAGE Request;
163 PIO_ERROR_LOG_MESSAGE Message;
164 PIO_ERROR_LOG_PACKET Packet;
165 KIRQL Irql;
166 NTSTATUS Status;
167
168 UCHAR Buffer[256];
169 POBJECT_NAME_INFORMATION ObjectNameInfo;
170 ULONG ReturnedLength;
171 PWCHAR DriverName;
172 ULONG DriverNameLength;
173
174 DPRINT ("IopLogWorker() called\n");
175
176 /* Release the work item */
177 ExFreePool (Parameter);
178
179 /* Connect to the error log port */
180 if (IopLogPortConnected == FALSE)
181 {
182 if (IopConnectLogPort () == FALSE)
183 {
184 IopRestartLogWorker ();
185 return;
186 }
187
188 IopLogPortConnected = TRUE;
189 }
190
191 while (TRUE)
192 {
193 /* Remove last entry from the list */
194 KeAcquireSpinLock (&IopLogListLock,
195 &Irql);
196
197 if (!IsListEmpty (&IopLogListHead))
198 {
199 LogEntry = CONTAINING_RECORD (IopLogListHead.Blink,
200 ERROR_LOG_ENTRY,
201 Entry);
202 RemoveEntryList (&LogEntry->Entry);
203 }
204 else
205 {
206 LogEntry = NULL;
207 }
208
209 KeReleaseSpinLock (&IopLogListLock,
210 Irql);
211
212 if (LogEntry == NULL)
213 {
214 DPRINT ("No message in log list\n");
215 break;
216 }
217
218 /* Get pointer to the log packet */
219 Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
220
221
222 /* Get driver or device name */
223 ObjectNameInfo = (POBJECT_NAME_INFORMATION)Buffer;
224 Status = ObQueryNameString (LogEntry->IoObject,
225 ObjectNameInfo,
226 256,
227 &ReturnedLength);
228 if (NT_SUCCESS(Status))
229 {
230 DPRINT ("ReturnedLength: %lu\n", ReturnedLength);
231 DPRINT ("Length: %hu\n", ObjectNameInfo->Name.Length);
232 DPRINT ("MaximumLength: %hu\n", ObjectNameInfo->Name.MaximumLength);
233 DPRINT ("Object: %wZ\n", &ObjectNameInfo->Name);
234
235 DriverName = wcsrchr(ObjectNameInfo->Name.Buffer, L'\\');
236 if (DriverName != NULL)
237 DriverName++;
238 else
239 DriverName = ObjectNameInfo->Name.Buffer;
240
241 DriverNameLength = wcslen (DriverName) * sizeof(WCHAR);
242 DPRINT ("Driver name '%S'\n", DriverName);
243 }
244 else
245 {
246 DriverName = NULL;
247 DriverNameLength = 0;
248 }
249
250 /* Allocate request buffer */
251 Request = ExAllocatePool (NonPagedPool,
252 sizeof(PORT_MESSAGE) + PORT_MAXIMUM_MESSAGE_LENGTH);
253 if (Request == NULL)
254 {
255 DPRINT ("Failed to allocate request buffer!\n");
256
257 /* Requeue log message and restart the worker */
258 ExInterlockedInsertTailList (&IopLogListHead,
259 &LogEntry->Entry,
260 &IopLogListLock);
261 IopRestartLogWorker ();
262
263 return;
264 }
265
266 /* Initialize the log message */
267 Message = (PIO_ERROR_LOG_MESSAGE)(Request + 1);
268 Message->Type = IO_TYPE_ERROR_MESSAGE;
269 Message->Size =
270 sizeof(IO_ERROR_LOG_MESSAGE) - sizeof(IO_ERROR_LOG_PACKET) +
271 LogEntry->PacketSize + DriverNameLength;
272 Message->DriverNameLength = (USHORT)DriverNameLength;
273 Message->TimeStamp.QuadPart = LogEntry->TimeStamp.QuadPart;
274 Message->DriverNameOffset = (DriverName != NULL) ? LogEntry->PacketSize : 0;
275
276 /* Copy error log packet */
277 RtlCopyMemory (&Message->EntryData,
278 Packet,
279 LogEntry->PacketSize);
280
281 /* Copy driver or device name */
282 RtlCopyMemory ((PVOID)((ULONG_PTR)Message + Message->DriverNameOffset),
283 DriverName,
284 DriverNameLength);
285
286 DPRINT ("SequenceNumber %lx\n", Packet->SequenceNumber);
287
288 Request->u1.s1.DataLength = Message->Size;
289 Request->u1.s1.TotalLength =
290 Request->u1.s1.DataLength + sizeof(PPORT_MESSAGE);
291
292 /* Send the error message to the log port */
293 Status = ZwRequestPort (IopLogPort,
294 Request);
295
296 /* Release request buffer */
297 ExFreePool (Request);
298
299 if (!NT_SUCCESS(Status))
300 {
301 DPRINT ("ZwRequestPort() failed (Status %lx)\n", Status);
302
303 /* Requeue log message and restart the worker */
304 ExInterlockedInsertTailList (&IopLogListHead,
305 &LogEntry->Entry,
306 &IopLogListLock);
307 IopRestartLogWorker ();
308
309 return;
310 }
311
312 /* Release error log entry */
313 KeAcquireSpinLock (&IopAllocationLock,
314 &Irql);
315
316 IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY));
317 ExFreePool (LogEntry);
318
319 KeReleaseSpinLock (&IopAllocationLock,
320 Irql);
321 }
322
323 IopLogWorkerRunning = FALSE;
324
325 DPRINT ("IopLogWorker() done\n");
326 }
327
328
329 /*
330 * @implemented
331 */
332 PVOID STDCALL
333 IoAllocateErrorLogEntry (IN PVOID IoObject,
334 IN UCHAR EntrySize)
335 {
336 PERROR_LOG_ENTRY LogEntry;
337 ULONG LogEntrySize;
338 KIRQL Irql;
339
340 DPRINT("IoAllocateErrorLogEntry() called\n");
341
342 if (IoObject == NULL)
343 return NULL;
344
345 KeAcquireSpinLock (&IopAllocationLock,
346 &Irql);
347
348 if (IopTotalLogSize > PAGE_SIZE)
349 {
350 KeReleaseSpinLock (&IopAllocationLock,
351 Irql);
352 return NULL;
353 }
354
355 LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize;
356 LogEntry = ExAllocatePool (NonPagedPool,
357 LogEntrySize);
358 if (LogEntry == NULL)
359 {
360 KeReleaseSpinLock (&IopAllocationLock,
361 Irql);
362 return NULL;
363 }
364
365 IopTotalLogSize += EntrySize;
366
367 LogEntry->IoObject = IoObject;
368 LogEntry->PacketSize = LogEntrySize;
369
370 KeReleaseSpinLock (&IopAllocationLock,
371 Irql);
372
373 return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
374 }
375
376
377 /*
378 * @implemented
379 */
380 VOID STDCALL
381 IoFreeErrorLogEntry(IN PVOID ElEntry)
382 {
383 PERROR_LOG_ENTRY LogEntry;
384 KIRQL Irql;
385
386 DPRINT("IoFreeErrorLogEntry() called\n");
387
388 if (ElEntry == NULL)
389 return;
390
391 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
392
393 KeAcquireSpinLock(&IopAllocationLock,
394 &Irql);
395
396 IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY));
397 ExFreePool(LogEntry);
398
399 KeReleaseSpinLock(&IopAllocationLock,
400 Irql);
401 }
402
403
404 /*
405 * @implemented
406 */
407 VOID STDCALL
408 IoWriteErrorLogEntry (IN PVOID ElEntry)
409 {
410 PWORK_QUEUE_ITEM LogWorkItem;
411 PERROR_LOG_ENTRY LogEntry;
412 KIRQL Irql;
413
414 DPRINT("IoWriteErrorLogEntry() called\n");
415
416 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
417
418 /* Get time stamp */
419 KeQuerySystemTime (&LogEntry->TimeStamp);
420
421 KeAcquireSpinLock (&IopLogListLock,
422 &Irql);
423
424 InsertHeadList (&IopLogListHead,
425 &LogEntry->Entry);
426
427 if (IopLogWorkerRunning == FALSE)
428 {
429 LogWorkItem = ExAllocatePool (NonPagedPool,
430 sizeof(WORK_QUEUE_ITEM));
431 if (LogWorkItem != NULL)
432 {
433 ExInitializeWorkItem (LogWorkItem,
434 IopLogWorker,
435 LogWorkItem);
436
437 ExQueueWorkItem (LogWorkItem,
438 DelayedWorkQueue);
439
440 IopLogWorkerRunning = TRUE;
441 }
442 }
443
444 KeReleaseSpinLock (&IopLogListLock,
445 Irql);
446
447 DPRINT("IoWriteErrorLogEntry() done\n");
448 }
449
450 VOID
451 STDCALL
452 IopFreeApc(PKAPC Apc,
453 PKNORMAL_ROUTINE *NormalRoutine,
454 PVOID *NormalContext,
455 PVOID *SystemArgument1,
456 PVOID *SystemArgument2)
457 {
458 /* Free the APC */
459 ExFreePool(Apc);
460 }
461
462 VOID
463 STDCALL
464 IopRaiseHardError(PKAPC Apc,
465 PKNORMAL_ROUTINE *NormalRoutine,
466 PVOID *NormalContext,
467 PVOID *SystemArgument1,
468 PVOID *SystemArgument2)
469 {
470 PIRP Irp = (PIRP)NormalContext;
471 //PVPB Vpb = (PVPB)SystemArgument1;
472 //PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument2;
473
474 /* FIXME: UNIMPLEMENTED */
475 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
476 Irp->IoStatus.Information = 0;
477 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
478 }
479
480 /*
481 * @implemented
482 */
483 VOID
484 STDCALL
485 IoRaiseHardError(PIRP Irp,
486 PVPB Vpb,
487 PDEVICE_OBJECT RealDeviceObject)
488 {
489 PETHREAD Thread = (PETHREAD)&Irp->Tail.Overlay.Thread;
490 PKAPC ErrorApc;
491
492 /* Don't do anything if hard errors are disabled on the thread */
493 if (Thread->HardErrorsAreDisabled)
494 {
495 /* Complete the request */
496 Irp->IoStatus.Information = 0;
497 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
498 return;
499 }
500
501 /* Setup an APC */
502 ErrorApc = ExAllocatePoolWithTag(NonPagedPool,
503 sizeof(KAPC),
504 TAG('K', 'A', 'P', 'C'));
505 KeInitializeApc(ErrorApc,
506 &Thread->Tcb,
507 Irp->ApcEnvironment,
508 NULL,
509 (PKRUNDOWN_ROUTINE)IopFreeApc,
510 (PKNORMAL_ROUTINE)IopRaiseHardError,
511 KernelMode,
512 Irp);
513
514 /* Queue an APC to deal with the error (see osr documentation) */
515 KeInsertQueueApc(ErrorApc, Vpb, RealDeviceObject, 0);
516 }
517
518 /*
519 * @unimplemented
520 */
521 BOOLEAN
522 STDCALL
523 IoRaiseInformationalHardError(NTSTATUS ErrorStatus,
524 PUNICODE_STRING String,
525 PKTHREAD Thread)
526 {
527 UNIMPLEMENTED;
528 return(FALSE);
529 }
530
531 /**********************************************************************
532 * NAME EXPORTED
533 * IoSetThreadHardErrorMode@4
534 *
535 * ARGUMENTS
536 * HardErrorEnabled
537 * TRUE : enable hard errors processing;
538 * FALSE: do NOT process hard errors.
539 *
540 * RETURN VALUE
541 * Previous value for the current thread's hard errors
542 * processing policy.
543 *
544 * @implemented
545 */
546 BOOLEAN
547 STDCALL
548 IoSetThreadHardErrorMode(IN BOOLEAN HardErrorEnabled)
549 {
550 BOOLEAN PreviousHEM = (BOOLEAN)(NtCurrentTeb()->HardErrorDisabled);
551
552 NtCurrentTeb()->HardErrorDisabled = ((TRUE == HardErrorEnabled) ? FALSE : TRUE);
553
554 return((TRUE == PreviousHEM) ? FALSE : TRUE);
555 }
556
557 /* EOF */