[EVENTLOG]
[reactos.git] / reactos / base / services / eventlog / file.c
1 /*
2 * PROJECT: ReactOS kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/services/eventlog/file.c
5 * PURPOSE: Event logging service
6 * COPYRIGHT: Copyright 2005 Saveliy Tretiakov
7 * Michael Martin
8 * Hermes Belusca-Maito
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include "eventlog.h"
14
15 #include <ndk/iofuncs.h>
16 #include <ndk/kefuncs.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 /* LOG FILE LIST - GLOBALS ***************************************************/
22
23 static LIST_ENTRY LogFileListHead;
24 static CRITICAL_SECTION LogFileListCs;
25
26 /* LOG FILE LIST - FUNCTIONS *************************************************/
27
28 VOID LogfCloseAll(VOID)
29 {
30 EnterCriticalSection(&LogFileListCs);
31
32 while (!IsListEmpty(&LogFileListHead))
33 {
34 LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE);
35 }
36
37 LeaveCriticalSection(&LogFileListCs);
38
39 DeleteCriticalSection(&LogFileListCs);
40 }
41
42 VOID LogfListInitialize(VOID)
43 {
44 InitializeCriticalSection(&LogFileListCs);
45 InitializeListHead(&LogFileListHead);
46 }
47
48 PLOGFILE LogfListItemByName(LPCWSTR Name)
49 {
50 PLIST_ENTRY CurrentEntry;
51 PLOGFILE Item, Result = NULL;
52
53 ASSERT(Name);
54
55 EnterCriticalSection(&LogFileListCs);
56
57 CurrentEntry = LogFileListHead.Flink;
58 while (CurrentEntry != &LogFileListHead)
59 {
60 Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
61
62 if (Item->LogName && !_wcsicmp(Item->LogName, Name))
63 {
64 Result = Item;
65 break;
66 }
67
68 CurrentEntry = CurrentEntry->Flink;
69 }
70
71 LeaveCriticalSection(&LogFileListCs);
72 return Result;
73 }
74
75 #if 0
76 /* Index starting from 1 */
77 DWORD LogfListItemIndexByName(LPCWSTR Name)
78 {
79 PLIST_ENTRY CurrentEntry;
80 DWORD Result = 0;
81 DWORD i = 1;
82
83 ASSERT(Name);
84
85 EnterCriticalSection(&LogFileListCs);
86
87 CurrentEntry = LogFileListHead.Flink;
88 while (CurrentEntry != &LogFileListHead)
89 {
90 PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
91
92 if (Item->LogName && !_wcsicmp(Item->LogName, Name))
93 {
94 Result = i;
95 break;
96 }
97
98 CurrentEntry = CurrentEntry->Flink;
99 i++;
100 }
101
102 LeaveCriticalSection(&LogFileListCs);
103 return Result;
104 }
105 #endif
106
107 /* Index starting from 1 */
108 PLOGFILE LogfListItemByIndex(DWORD Index)
109 {
110 PLIST_ENTRY CurrentEntry;
111 PLOGFILE Result = NULL;
112 DWORD i = 1;
113
114 EnterCriticalSection(&LogFileListCs);
115
116 CurrentEntry = LogFileListHead.Flink;
117 while (CurrentEntry != &LogFileListHead)
118 {
119 if (i == Index)
120 {
121 Result = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
122 break;
123 }
124
125 CurrentEntry = CurrentEntry->Flink;
126 i++;
127 }
128
129 LeaveCriticalSection(&LogFileListCs);
130 return Result;
131 }
132
133 DWORD LogfListItemCount(VOID)
134 {
135 PLIST_ENTRY CurrentEntry;
136 DWORD i = 0;
137
138 EnterCriticalSection(&LogFileListCs);
139
140 CurrentEntry = LogFileListHead.Flink;
141 while (CurrentEntry != &LogFileListHead)
142 {
143 CurrentEntry = CurrentEntry->Flink;
144 i++;
145 }
146
147 LeaveCriticalSection(&LogFileListCs);
148 return i;
149 }
150
151 static VOID
152 LogfListAddItem(PLOGFILE Item)
153 {
154 EnterCriticalSection(&LogFileListCs);
155 InsertTailList(&LogFileListHead, &Item->ListEntry);
156 LeaveCriticalSection(&LogFileListCs);
157 }
158
159 static VOID
160 LogfListRemoveItem(PLOGFILE Item)
161 {
162 EnterCriticalSection(&LogFileListCs);
163 RemoveEntryList(&Item->ListEntry);
164 LeaveCriticalSection(&LogFileListCs);
165 }
166
167
168 /* GLOBALS *******************************************************************/
169
170 static const EVENTLOGEOF EOFRecord =
171 {
172 sizeof(EOFRecord),
173 0x11111111, 0x22222222, 0x33333333, 0x44444444,
174 0, 0, 0, 0,
175 sizeof(EOFRecord)
176 };
177
178 /* FUNCTIONS *****************************************************************/
179
180 static NTSTATUS
181 ReadLogBuffer(IN PLOGFILE LogFile,
182 OUT PIO_STATUS_BLOCK IoStatusBlock,
183 OUT PVOID Buffer,
184 IN ULONG Length,
185 IN PLARGE_INTEGER ByteOffset,
186 OUT PLARGE_INTEGER NextOffset OPTIONAL)
187 {
188 NTSTATUS Status;
189 ULONG BufSize;
190 LARGE_INTEGER FileOffset;
191
192 ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
193 // ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize);
194 ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
195
196 if (NextOffset)
197 NextOffset->QuadPart = 0LL;
198
199 /* Read the first part of the buffer */
200 FileOffset = *ByteOffset;
201 BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart);
202
203 Status = NtReadFile(LogFile->hFile,
204 NULL,
205 NULL,
206 NULL,
207 IoStatusBlock,
208 Buffer,
209 BufSize,
210 &FileOffset,
211 NULL);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
215 return Status;
216 }
217
218 if (Length > BufSize)
219 {
220 ULONG_PTR Information = IoStatusBlock->Information;
221
222 /*
223 * The buffer was splitted in two, its second part
224 * is to be read at the beginning of the log.
225 */
226 Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
227 BufSize = Length - BufSize;
228 FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
229
230 Status = NtReadFile(LogFile->hFile,
231 NULL,
232 NULL,
233 NULL,
234 IoStatusBlock,
235 Buffer,
236 BufSize,
237 &FileOffset,
238 NULL);
239 if (!NT_SUCCESS(Status))
240 {
241 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
242 return Status;
243 }
244 /* Add the read number of bytes from the first read */
245 IoStatusBlock->Information += Information;
246 }
247
248 /* We return the offset just after the end of the read buffer */
249 if (NextOffset)
250 NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
251
252 return Status;
253 }
254
255 static NTSTATUS
256 WriteLogBuffer(IN PLOGFILE LogFile,
257 OUT PIO_STATUS_BLOCK IoStatusBlock,
258 IN PVOID Buffer,
259 IN ULONG Length,
260 IN PLARGE_INTEGER ByteOffset,
261 OUT PLARGE_INTEGER NextOffset OPTIONAL)
262 {
263 NTSTATUS Status;
264 ULONG BufSize;
265 LARGE_INTEGER FileOffset;
266
267 ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
268 ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize);
269 ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
270
271 if (NextOffset)
272 NextOffset->QuadPart = 0LL;
273
274 /* Write the first part of the buffer */
275 FileOffset = *ByteOffset;
276 BufSize = min(Length, LogFile->CurrentSize /* LogFile->Header.MaxSize */ - FileOffset.QuadPart);
277
278 Status = NtWriteFile(LogFile->hFile,
279 NULL,
280 NULL,
281 NULL,
282 IoStatusBlock,
283 Buffer,
284 BufSize,
285 &FileOffset,
286 NULL);
287 if (!NT_SUCCESS(Status))
288 {
289 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
290 return Status;
291 }
292
293 if (Length > BufSize)
294 {
295 ULONG_PTR Information = IoStatusBlock->Information;
296
297 /*
298 * The buffer was splitted in two, its second part is written
299 * at the beginning of the log.
300 */
301 Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
302 BufSize = Length - BufSize;
303 FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
304
305 Status = NtWriteFile(LogFile->hFile,
306 NULL,
307 NULL,
308 NULL,
309 IoStatusBlock,
310 Buffer,
311 BufSize,
312 &FileOffset,
313 NULL);
314 if (!NT_SUCCESS(Status))
315 {
316 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
317 return Status;
318 }
319 /* Add the written number of bytes from the first write */
320 IoStatusBlock->Information += Information;
321
322 /* The log wraps */
323 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
324 }
325
326 /* We return the offset just after the end of the written buffer */
327 if (NextOffset)
328 NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
329
330 return Status;
331 }
332
333
334 /* Returns 0 if nothing is found */
335 static ULONG
336 LogfOffsetByNumber(PLOGFILE LogFile,
337 DWORD RecordNumber)
338 {
339 DWORD i;
340
341 for (i = 0; i < LogFile->OffsetInfoNext; i++)
342 {
343 if (LogFile->OffsetInfo[i].EventNumber == RecordNumber)
344 return LogFile->OffsetInfo[i].EventOffset;
345 }
346 return 0;
347 }
348
349 static BOOL
350 LogfAddOffsetInformation(PLOGFILE LogFile,
351 ULONG ulNumber,
352 ULONG ulOffset)
353 {
354 PVOID NewOffsetInfo;
355
356 if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize)
357 {
358 NewOffsetInfo = HeapReAlloc(GetProcessHeap(),
359 HEAP_ZERO_MEMORY,
360 LogFile->OffsetInfo,
361 (LogFile->OffsetInfoSize + 64) *
362 sizeof(EVENT_OFFSET_INFO));
363
364 if (!NewOffsetInfo)
365 {
366 DPRINT1("Cannot reallocate heap.\n");
367 return FALSE;
368 }
369
370 LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo;
371 LogFile->OffsetInfoSize += 64;
372 }
373
374 LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber;
375 LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset;
376 LogFile->OffsetInfoNext++;
377
378 return TRUE;
379 }
380
381 static BOOL
382 LogfDeleteOffsetInformation(PLOGFILE LogFile,
383 ULONG ulNumberMin,
384 ULONG ulNumberMax)
385 {
386 DWORD i;
387
388 if (ulNumberMin > ulNumberMax)
389 return FALSE;
390
391 /* Remove records ulNumberMin to ulNumberMax inclusive */
392 while (ulNumberMin <= ulNumberMax)
393 {
394 /*
395 * As the offset information is listed in increasing order, and we want
396 * to keep the list without holes, we demand that ulNumberMin is the first
397 * element in the list.
398 */
399 if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber)
400 return FALSE;
401
402 /*
403 * RtlMoveMemory(&LogFile->OffsetInfo[0],
404 * &LogFile->OffsetInfo[1],
405 * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
406 */
407 for (i = 0; i < LogFile->OffsetInfoNext - 1; i++)
408 {
409 LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber;
410 LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset;
411 }
412 LogFile->OffsetInfoNext--;
413
414 /* Go to the next offset information */
415 ulNumberMin++;
416 }
417
418 return TRUE;
419 }
420
421 static NTSTATUS
422 LogfInitializeNew(PLOGFILE LogFile,
423 ULONG ulMaxSize,
424 ULONG ulRetention)
425 {
426 NTSTATUS Status;
427 IO_STATUS_BLOCK IoStatusBlock;
428 LARGE_INTEGER FileOffset;
429 EVENTLOGEOF EofRec;
430
431 /* Initialize the event log header */
432 RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER));
433
434 LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER);
435 LogFile->Header.Signature = LOGFILE_SIGNATURE;
436 LogFile->Header.MajorVersion = MAJORVER;
437 LogFile->Header.MinorVersion = MINORVER;
438
439 /* Set the offset to the oldest record */
440 LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
441 /* Set the offset to the ELF_EOF_RECORD */
442 LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER);
443 /* Set the number of the next record that will be added */
444 LogFile->Header.CurrentRecordNumber = 1;
445 /* The event log is empty, there is no record so far */
446 LogFile->Header.OldestRecordNumber = 0;
447
448 // FIXME: Windows' EventLog log file sizes are always multiple of 64kB
449 // but that does not mean the real log size is == file size.
450
451 /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */
452 LogFile->Header.MaxSize = ROUND_UP(ulMaxSize, sizeof(ULONG));
453 LogFile->CurrentSize = LogFile->Header.MaxSize;
454
455 LogFile->Header.Flags = 0;
456 LogFile->Header.Retention = ulRetention;
457 LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER);
458
459 /* Write the header */
460 SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN);
461 SetEndOfFile(LogFile->hFile);
462
463 FileOffset.QuadPart = 0LL;
464 Status = NtWriteFile(LogFile->hFile,
465 NULL,
466 NULL,
467 NULL,
468 &IoStatusBlock,
469 &LogFile->Header,
470 sizeof(EVENTLOGHEADER),
471 &FileOffset,
472 NULL);
473 if (!NT_SUCCESS(Status))
474 {
475 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
476 return Status;
477 }
478
479 /* Initialize the ELF_EOF_RECORD and write it */
480 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
481 EofRec.BeginRecord = LogFile->Header.StartOffset;
482 EofRec.EndRecord = LogFile->Header.EndOffset;
483 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
484 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
485
486 Status = NtWriteFile(LogFile->hFile,
487 NULL,
488 NULL,
489 NULL,
490 &IoStatusBlock,
491 &EofRec,
492 sizeof(EofRec),
493 NULL,
494 NULL);
495 if (!NT_SUCCESS(Status))
496 {
497 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
498 return Status;
499 }
500
501 Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock);
502 if (!NT_SUCCESS(Status))
503 {
504 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status);
505 return Status;
506 }
507
508 return STATUS_SUCCESS;
509 }
510
511 static NTSTATUS
512 LogfInitializeExisting(PLOGFILE LogFile,
513 BOOLEAN Backup)
514 {
515 NTSTATUS Status;
516 IO_STATUS_BLOCK IoStatusBlock;
517 LARGE_INTEGER FileOffset, NextOffset;
518 LARGE_INTEGER LogFileSize;
519 DWORD dwRecordsNumber = 0;
520 ULONG RecOffset;
521 PDWORD pdwRecSize2;
522 EVENTLOGEOF EofRec;
523 EVENTLOGRECORD RecBuf;
524 PEVENTLOGRECORD pRecBuf;
525 BOOLEAN Wrapping = FALSE;
526 BOOLEAN IsLogDirty = FALSE;
527
528 DPRINT("Initializing LogFile %S\n", LogFile->LogName);
529
530 /* Read the log header */
531 FileOffset.QuadPart = 0LL;
532 Status = NtReadFile(LogFile->hFile,
533 NULL,
534 NULL,
535 NULL,
536 &IoStatusBlock,
537 &LogFile->Header,
538 sizeof(EVENTLOGHEADER),
539 &FileOffset,
540 NULL);
541 if (!NT_SUCCESS(Status))
542 {
543 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
544 return STATUS_EVENTLOG_FILE_CORRUPT;
545 }
546 if (IoStatusBlock.Information != sizeof(EVENTLOGHEADER))
547 {
548 DPRINT("EventLog: Invalid file %S.\n", LogFile->FileName);
549 return STATUS_EVENTLOG_FILE_CORRUPT;
550 }
551
552 /* Header validity checks */
553
554 if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) ||
555 LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER))
556 {
557 DPRINT("EventLog: Invalid header size in %S.\n", LogFile->FileName);
558 return STATUS_EVENTLOG_FILE_CORRUPT;
559 }
560
561 if (LogFile->Header.Signature != LOGFILE_SIGNATURE)
562 {
563 DPRINT("EventLog: Invalid signature %x in %S.\n",
564 LogFile->Header.Signature, LogFile->FileName);
565 return STATUS_EVENTLOG_FILE_CORRUPT;
566 }
567
568 IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY);
569
570 /* If the log is a backup log that is dirty, then it is corrupted */
571 if (Backup && IsLogDirty)
572 {
573 DPRINT("EventLog: Backup log %S is dirty.\n", LogFile->FileName);
574 return STATUS_EVENTLOG_FILE_CORRUPT;
575 }
576
577 /*
578 * Retrieve the log file size and check whether the file is not too large;
579 * this log format only supports files of theoretical size < 0xFFFFFFFF .
580 */
581 if (!GetFileSizeEx(LogFile->hFile, &LogFileSize))
582 return I_RpcMapWin32Status(GetLastError());
583
584 if (LogFileSize.HighPart != 0)
585 {
586 DPRINT1("EventLog: Log %S is too large.\n", LogFile->FileName);
587 // return STATUS_FILE_TOO_LARGE;
588 return STATUS_EVENTLOG_FILE_CORRUPT;
589 }
590
591 LogFile->CurrentSize = LogFileSize.LowPart; // LogFileSize.QuadPart;
592
593 /* Adjust the log maximum size if needed */
594 if (LogFile->CurrentSize > LogFile->Header.MaxSize)
595 LogFile->Header.MaxSize = LogFile->CurrentSize;
596
597 /*
598 * In a non-backup dirty log, the most up-to-date information about
599 * the Start/End offsets and the Oldest and Current event record numbers
600 * are found in the EOF record. We need to locate the EOF record without
601 * relying on the log header's EndOffset, then patch the log header with
602 * the values from the EOF record.
603 */
604 if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) &&
605 (LogFile->Header.EndOffset < LogFile->CurrentSize) &&
606 (LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0
607 {
608 /* The header EOF offset may be valid, try to start with it */
609 RecOffset = LogFile->Header.EndOffset;
610 }
611 else
612 {
613 /* The header EOF offset could not be valid, so start from the beginning */
614 RecOffset = sizeof(EVENTLOGHEADER);
615 }
616
617 FileOffset.QuadPart = RecOffset;
618 Wrapping = FALSE;
619
620 for (;;)
621 {
622 if (Wrapping && FileOffset.QuadPart >= RecOffset)
623 {
624 DPRINT1("EOF record not found!\n");
625 return STATUS_EVENTLOG_FILE_CORRUPT;
626 }
627
628 /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
629 Status = ReadLogBuffer(LogFile,
630 &IoStatusBlock,
631 &EofRec,
632 EVENTLOGEOF_SIZE_FIXED,
633 &FileOffset,
634 NULL);
635 if (!NT_SUCCESS(Status))
636 {
637 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
638 return STATUS_EVENTLOG_FILE_CORRUPT;
639 }
640 if (IoStatusBlock.Information != EVENTLOGEOF_SIZE_FIXED)
641 {
642 DPRINT1("Cannot read at most an EOF record!\n");
643 return STATUS_EVENTLOG_FILE_CORRUPT;
644 }
645
646 /* Is it an EVENTLOGEOF record? */
647 if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED)
648 {
649 DPRINT("Found EOF record at %llx\n", FileOffset.QuadPart);
650
651 /* Got it! Break the loop and continue */
652 break;
653 }
654
655 /* No, continue looping */
656 if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord))
657 FileOffset.QuadPart += sizeof(ULONG);
658 else
659 if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
660 FileOffset.QuadPart += 2*sizeof(ULONG);
661 else
662 if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
663 FileOffset.QuadPart += 3*sizeof(ULONG);
664 else
665 if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
666 FileOffset.QuadPart += 4*sizeof(ULONG);
667 else
668 FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED
669
670 if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */)
671 {
672 /* Wrap the offset */
673 FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER);
674 Wrapping = TRUE;
675 }
676 }
677 /*
678 * The only way to be there is to have found a valid EOF record.
679 * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT
680 * was returned.
681 */
682
683 /* Read the full EVENTLOGEOF (may wrap) and validate it */
684 Status = ReadLogBuffer(LogFile,
685 &IoStatusBlock,
686 &EofRec,
687 sizeof(EofRec),
688 &FileOffset,
689 NULL);
690 if (!NT_SUCCESS(Status))
691 {
692 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
693 return STATUS_EVENTLOG_FILE_CORRUPT;
694 }
695 if (IoStatusBlock.Information != sizeof(EofRec))
696 {
697 DPRINT1("Cannot read the full EOF record!\n");
698 return STATUS_EVENTLOG_FILE_CORRUPT;
699 }
700
701 /* Complete validity checks */
702 if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) ||
703 (EofRec.EndRecord != FileOffset.QuadPart))
704 {
705 DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected %x %x!\n", FileOffset.QuadPart,
706 EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning,
707 EofRec.EndRecord, FileOffset.QuadPart,
708 EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning);
709 DPRINT1("RecordSizeEnd = %x\n", EofRec.RecordSizeEnd);
710 DPRINT1("RecordSizeBeginning = %x\n", EofRec.RecordSizeBeginning);
711 DPRINT1("EndRecord = %x\n", EofRec.EndRecord);
712 return STATUS_EVENTLOG_FILE_CORRUPT;
713 }
714
715 /* The EOF record is valid, break the loop and continue */
716
717 /* If the log is not dirty, the header values should correspond to the EOF ones */
718 if (!IsLogDirty)
719 {
720 if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) ||
721 (LogFile->Header.EndOffset != EofRec.EndRecord) ||
722 (LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) ||
723 (LogFile->Header.OldestRecordNumber != EofRec.OldestRecordNumber) )
724 {
725 DPRINT1("\n"
726 "Log header or EOF record is corrupted:\n"
727 " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n"
728 " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n",
729 LogFile->Header.StartOffset, EofRec.BeginRecord,
730 LogFile->Header.EndOffset , EofRec.EndRecord,
731 LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber,
732 LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber);
733
734 return STATUS_EVENTLOG_FILE_CORRUPT;
735 }
736 }
737
738 /* If the log is dirty, patch the log header with the values from the EOF record */
739 if (!Backup && IsLogDirty)
740 {
741 LogFile->Header.StartOffset = EofRec.BeginRecord;
742 LogFile->Header.EndOffset = EofRec.EndRecord;
743 LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber;
744 LogFile->Header.OldestRecordNumber = EofRec.OldestRecordNumber;
745 }
746
747 /*
748 * FIXME! During operations the EOF record is the one that is the most
749 * updated (its Oldest & Current record numbers are always up-to
750 * date) while the ones from the header may be unsync. When closing
751 * (or flushing?) the event log, the header's record numbers get
752 * updated with the same values as the ones stored in the EOF record.
753 */
754
755 /* Verify Start/End offsets boundaries */
756
757 if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) ||
758 (LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0
759 {
760 DPRINT("EventLog: Invalid start offset %x in %S.\n",
761 LogFile->Header.StartOffset, LogFile->FileName);
762 return STATUS_EVENTLOG_FILE_CORRUPT;
763 }
764 if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) ||
765 (LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0
766 {
767 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
768 LogFile->Header.EndOffset, LogFile->FileName);
769 return STATUS_EVENTLOG_FILE_CORRUPT;
770 }
771
772 if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) &&
773 (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
774 {
775 /*
776 * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
777 * it should point to a non-splitted EVENTLOGRECORD.
778 */
779 DPRINT("EventLog: Invalid start offset %x in %S.\n",
780 LogFile->Header.StartOffset, LogFile->FileName);
781 return STATUS_EVENTLOG_FILE_CORRUPT;
782 }
783
784 if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
785 (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
786 {
787 /*
788 * In non-wrapping case, there must be enough space between StartOffset
789 * and EndOffset to contain at least a full EVENTLOGRECORD.
790 */
791 DPRINT("EventLog: Invalid start offset %x or end offset %x in %S.\n",
792 LogFile->Header.StartOffset, LogFile->Header.EndOffset, LogFile->FileName);
793 return STATUS_EVENTLOG_FILE_CORRUPT;
794 }
795
796 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
797 {
798 /*
799 * Non-wrapping case: the (wrapping) free space starting at EndOffset
800 * must be able to contain an EVENTLOGEOF.
801 */
802 if (LogFile->Header.MaxSize - LogFile->Header.EndOffset +
803 LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF))
804 {
805 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
806 LogFile->Header.EndOffset, LogFile->FileName);
807 return STATUS_EVENTLOG_FILE_CORRUPT;
808 }
809 }
810 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
811 {
812 /*
813 * Wrapping case: the free space between EndOffset and StartOffset
814 * must be able to contain an EVENTLOGEOF.
815 */
816 if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF))
817 {
818 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
819 LogFile->Header.EndOffset, LogFile->FileName);
820 return STATUS_EVENTLOG_FILE_CORRUPT;
821 }
822 }
823
824 /* Start enumerating the event records from the beginning */
825 RecOffset = LogFile->Header.StartOffset;
826 FileOffset.QuadPart = RecOffset;
827 Wrapping = FALSE;
828
829 // // FIXME! FIXME!
830 // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
831 // {
832 // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
833 // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
834 // }
835
836 DPRINT("StartOffset = %x, EndOffset = %x\n",
837 LogFile->Header.StartOffset, LogFile->Header.EndOffset);
838
839 /*
840 * For non-backup logs of size < MaxSize, reorganize the events such that
841 * they do not wrap as soon as we write new ones.
842 */
843 #if 0
844 if (!Backup)
845 {
846 pRecBuf = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length);
847 if (pRecBuf == NULL)
848 {
849 DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
850 goto Continue;
851 }
852
853 // TODO: Do the job!
854 }
855
856 Continue:
857
858 DPRINT("StartOffset = %x, EndOffset = %x\n",
859 LogFile->Header.StartOffset, LogFile->Header.EndOffset);
860 #endif
861
862 while (FileOffset.QuadPart != LogFile->Header.EndOffset)
863 {
864 if (Wrapping && FileOffset.QuadPart >= RecOffset)
865 {
866 /* We have finished enumerating all the event records */
867 break;
868 }
869
870 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
871 Status = NtReadFile(LogFile->hFile,
872 NULL,
873 NULL,
874 NULL,
875 &IoStatusBlock,
876 &RecBuf,
877 sizeof(RecBuf),
878 &FileOffset,
879 NULL);
880 if (!NT_SUCCESS(Status))
881 {
882 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
883 return STATUS_EVENTLOG_FILE_CORRUPT;
884 }
885 if (IoStatusBlock.Information != sizeof(RecBuf))
886 {
887 DPRINT("Length != sizeof(RecBuf)\n");
888 break;
889 }
890
891 if (RecBuf.Reserved != LOGFILE_SIGNATURE ||
892 RecBuf.Length < sizeof(EVENTLOGRECORD))
893 {
894 DPRINT("RecBuf problem\n");
895 break;
896 }
897
898 /* Allocate a full EVENTLOGRECORD (header + data) */
899 pRecBuf = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length);
900 if (pRecBuf == NULL)
901 {
902 DPRINT1("Cannot allocate heap!\n");
903 return STATUS_NO_MEMORY;
904 }
905
906 /* Attempt to read the full EVENTLOGRECORD (can wrap) */
907 Status = ReadLogBuffer(LogFile,
908 &IoStatusBlock,
909 pRecBuf,
910 RecBuf.Length,
911 &FileOffset,
912 &NextOffset);
913 if (!NT_SUCCESS(Status))
914 {
915 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
916 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
917 return STATUS_EVENTLOG_FILE_CORRUPT;
918 }
919 if (IoStatusBlock.Information != RecBuf.Length)
920 {
921 DPRINT1("Oh oh!!\n");
922 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
923 break;
924 }
925
926 // /* If OverWrittenRecords is TRUE and this record has already been read */
927 // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
928 // {
929 // RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
930 // break;
931 // }
932
933 pdwRecSize2 = (PDWORD)((ULONG_PTR)pRecBuf + RecBuf.Length - 4);
934
935 if (*pdwRecSize2 != RecBuf.Length)
936 {
937 DPRINT1("Invalid RecordSizeEnd of record %d (%x) in %S\n",
938 dwRecordsNumber, *pdwRecSize2, LogFile->LogName);
939 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
940 break;
941 }
942
943 DPRINT("Add new record %d - %x\n", pRecBuf->RecordNumber, FileOffset.QuadPart);
944
945 dwRecordsNumber++;
946
947 if (!LogfAddOffsetInformation(LogFile,
948 pRecBuf->RecordNumber,
949 FileOffset.QuadPart))
950 {
951 DPRINT1("LogfAddOffsetInformation() failed!\n");
952 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
953 return STATUS_EVENTLOG_FILE_CORRUPT;
954 }
955
956 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
957
958 if (NextOffset.QuadPart == LogFile->Header.EndOffset)
959 {
960 /* We have finished enumerating all the event records */
961 DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
962 break;
963 }
964
965 /*
966 * If this was the last event record before the end of the log file,
967 * the next one should start at the beginning of the log and the space
968 * between the last event record and the end of the file is padded.
969 */
970 if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD))
971 {
972 /* Wrap to the beginning of the log */
973 DPRINT("Wrap!\n");
974 NextOffset.QuadPart = sizeof(EVENTLOGHEADER);
975 }
976
977 /*
978 * If the next offset to read is below the current offset,
979 * this means we are wrapping.
980 */
981 if (FileOffset.QuadPart > NextOffset.QuadPart)
982 {
983 DPRINT("Wrapping = TRUE;\n");
984 Wrapping = TRUE;
985 }
986
987 /* Move the current offset */
988 FileOffset = NextOffset;
989 }
990
991 /* If the event log was empty, it will now contain one record */
992 if (dwRecordsNumber != 0 && LogFile->Header.OldestRecordNumber == 0)
993 LogFile->Header.OldestRecordNumber = 1;
994
995 LogFile->Header.CurrentRecordNumber = dwRecordsNumber + LogFile->Header.OldestRecordNumber;
996 if (LogFile->Header.CurrentRecordNumber == 0)
997 LogFile->Header.CurrentRecordNumber = 1;
998
999 if (!Backup)
1000 {
1001 FileOffset.QuadPart = 0LL;
1002 Status = NtWriteFile(LogFile->hFile,
1003 NULL,
1004 NULL,
1005 NULL,
1006 &IoStatusBlock,
1007 &LogFile->Header,
1008 sizeof(EVENTLOGHEADER),
1009 &FileOffset,
1010 NULL);
1011 if (!NT_SUCCESS(Status))
1012 {
1013 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
1014 return STATUS_EVENTLOG_FILE_CORRUPT;
1015 }
1016
1017 Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock);
1018 if (!NT_SUCCESS(Status))
1019 {
1020 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status);
1021 return STATUS_EVENTLOG_FILE_CORRUPT;
1022 }
1023 }
1024
1025 return STATUS_SUCCESS;
1026 }
1027
1028 NTSTATUS
1029 LogfCreate(PLOGFILE* LogFile,
1030 PCWSTR LogName,
1031 PUNICODE_STRING FileName,
1032 ULONG ulMaxSize,
1033 ULONG ulRetention,
1034 BOOLEAN Permanent,
1035 BOOLEAN Backup)
1036 {
1037 NTSTATUS Status = STATUS_SUCCESS;
1038 OBJECT_ATTRIBUTES ObjectAttributes;
1039 IO_STATUS_BLOCK IoStatusBlock;
1040 PLOGFILE pLogFile;
1041 SIZE_T LogNameLen;
1042 BOOLEAN CreateNew = FALSE;
1043
1044 pLogFile = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LOGFILE));
1045 if (!pLogFile)
1046 {
1047 DPRINT1("Cannot allocate heap!\n");
1048 return STATUS_NO_MEMORY;
1049 }
1050
1051 InitializeObjectAttributes(&ObjectAttributes,
1052 FileName,
1053 OBJ_CASE_INSENSITIVE,
1054 NULL,
1055 NULL);
1056
1057 Status = NtCreateFile(&pLogFile->hFile,
1058 Backup ? (GENERIC_READ | SYNCHRONIZE)
1059 : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE),
1060 &ObjectAttributes,
1061 &IoStatusBlock,
1062 NULL,
1063 FILE_ATTRIBUTE_NORMAL,
1064 FILE_SHARE_READ,
1065 Backup ? FILE_OPEN : FILE_OPEN_IF,
1066 FILE_SYNCHRONOUS_IO_NONALERT,
1067 NULL,
1068 0);
1069 if (!NT_SUCCESS(Status))
1070 {
1071 DPRINT1("Cannot create file %wZ (Status: 0x%08lx)\n", FileName, Status);
1072 goto Quit;
1073 }
1074
1075 CreateNew = (IoStatusBlock.Information == FILE_CREATED);
1076
1077 LogNameLen = (LogName ? wcslen(LogName) : 0) + 1;
1078 pLogFile->LogName = RtlAllocateHeap(GetProcessHeap(),
1079 HEAP_ZERO_MEMORY,
1080 LogNameLen * sizeof(WCHAR));
1081 if (pLogFile->LogName == NULL)
1082 {
1083 DPRINT1("Cannot allocate heap\n");
1084 Status = STATUS_NO_MEMORY;
1085 goto Quit;
1086 }
1087
1088 if (LogName)
1089 StringCchCopy(pLogFile->LogName, LogNameLen, LogName);
1090
1091 pLogFile->FileName = RtlAllocateHeap(GetProcessHeap(),
1092 HEAP_ZERO_MEMORY,
1093 /*(wcslen(FileName->Buffer) + 1) * sizeof(WCHAR)*/
1094 FileName->Length + sizeof(UNICODE_NULL));
1095 if (pLogFile->FileName == NULL)
1096 {
1097 DPRINT1("Cannot allocate heap\n");
1098 Status = STATUS_NO_MEMORY;
1099 goto Quit;
1100 }
1101
1102 StringCchCopy(pLogFile->FileName,
1103 /*wcslen(FileName->Buffer) + 1*/ (FileName->Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR),
1104 FileName->Buffer);
1105
1106 pLogFile->OffsetInfo = RtlAllocateHeap(GetProcessHeap(),
1107 HEAP_ZERO_MEMORY,
1108 sizeof(EVENT_OFFSET_INFO) * 64);
1109 if (pLogFile->OffsetInfo == NULL)
1110 {
1111 DPRINT1("Cannot allocate heap\n");
1112 Status = STATUS_NO_MEMORY;
1113 goto Quit;
1114 }
1115 pLogFile->OffsetInfoSize = 64;
1116 pLogFile->OffsetInfoNext = 0;
1117
1118 pLogFile->Permanent = Permanent;
1119
1120 // FIXME: Always use the regitry values for MaxSize & Retention,
1121 // even for existing logs!
1122
1123 // FIXME: On Windows, EventLog uses the MaxSize setting
1124 // from the registry itself; the MaxSize from the header
1125 // is just for information purposes.
1126
1127 if (CreateNew)
1128 Status = LogfInitializeNew(pLogFile, ulMaxSize, ulRetention);
1129 else
1130 Status = LogfInitializeExisting(pLogFile, Backup);
1131
1132 if (!NT_SUCCESS(Status))
1133 goto Quit;
1134
1135 RtlInitializeResource(&pLogFile->Lock);
1136
1137 LogfListAddItem(pLogFile);
1138
1139 Quit:
1140 if (!NT_SUCCESS(Status))
1141 {
1142 if ((pLogFile->hFile != NULL) && (pLogFile->hFile != INVALID_HANDLE_VALUE))
1143 NtClose(pLogFile->hFile);
1144
1145 if (pLogFile->OffsetInfo)
1146 RtlFreeHeap(GetProcessHeap(), 0, pLogFile->OffsetInfo);
1147
1148 if (pLogFile->FileName)
1149 RtlFreeHeap(GetProcessHeap(), 0, pLogFile->FileName);
1150
1151 if (pLogFile->LogName)
1152 RtlFreeHeap(GetProcessHeap(), 0, pLogFile->LogName);
1153
1154 RtlFreeHeap(GetProcessHeap(), 0, pLogFile);
1155 }
1156 else
1157 {
1158 *LogFile = pLogFile;
1159 }
1160
1161 return Status;
1162 }
1163
1164 VOID
1165 LogfClose(PLOGFILE LogFile,
1166 BOOLEAN ForceClose)
1167 {
1168 IO_STATUS_BLOCK IoStatusBlock;
1169
1170 if (LogFile == NULL)
1171 return;
1172
1173 if (!ForceClose && LogFile->Permanent)
1174 return;
1175
1176 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
1177
1178 NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock);
1179 NtClose(LogFile->hFile);
1180 LogfListRemoveItem(LogFile);
1181
1182 RtlDeleteResource(&LogFile->Lock);
1183
1184 RtlFreeHeap(GetProcessHeap(), 0, LogFile->LogName);
1185 RtlFreeHeap(GetProcessHeap(), 0, LogFile->FileName);
1186 RtlFreeHeap(GetProcessHeap(), 0, LogFile->OffsetInfo);
1187 RtlFreeHeap(GetProcessHeap(), 0, LogFile);
1188
1189 return;
1190 }
1191
1192
1193 static NTSTATUS
1194 ReadAnsiLogEntry(IN PLOGFILE LogFile,
1195 OUT PIO_STATUS_BLOCK IoStatusBlock,
1196 OUT PVOID Buffer,
1197 IN ULONG Length,
1198 IN PLARGE_INTEGER ByteOffset,
1199 OUT PLARGE_INTEGER NextOffset OPTIONAL)
1200 {
1201 NTSTATUS Status;
1202 PVOID UnicodeBuffer = NULL;
1203 PEVENTLOGRECORD Src, Dst;
1204 ANSI_STRING StringA;
1205 UNICODE_STRING StringW;
1206 PVOID SrcPtr, DstPtr;
1207 // DWORD dwRead;
1208 DWORD i;
1209 DWORD dwPadding;
1210 DWORD dwEntryLength;
1211 PDWORD pLength;
1212
1213 IoStatusBlock->Information = 0;
1214
1215 UnicodeBuffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Length);
1216 if (UnicodeBuffer == NULL)
1217 {
1218 DPRINT1("Alloc failed!\n");
1219 return STATUS_NO_MEMORY;
1220 }
1221
1222 Status = ReadLogBuffer(LogFile,
1223 IoStatusBlock,
1224 UnicodeBuffer,
1225 Length,
1226 ByteOffset,
1227 NextOffset);
1228 if (!NT_SUCCESS(Status))
1229 {
1230 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1231 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1232 goto done;
1233 }
1234 // dwRead = IoStatusBlock->Information;
1235
1236 Src = (PEVENTLOGRECORD)UnicodeBuffer;
1237 Dst = (PEVENTLOGRECORD)Buffer;
1238
1239 Dst->Reserved = Src->Reserved;
1240 Dst->RecordNumber = Src->RecordNumber;
1241 Dst->TimeGenerated = Src->TimeGenerated;
1242 Dst->TimeWritten = Src->TimeWritten;
1243 Dst->EventID = Src->EventID;
1244 Dst->EventType = Src->EventType;
1245 Dst->EventCategory = Src->EventCategory;
1246 Dst->NumStrings = Src->NumStrings;
1247 Dst->UserSidLength = Src->UserSidLength;
1248 Dst->DataLength = Src->DataLength;
1249
1250 SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD));
1251 DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD));
1252
1253 /* Convert the module name */
1254 RtlInitUnicodeString(&StringW, SrcPtr);
1255 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
1256 if (NT_SUCCESS(Status))
1257 {
1258 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
1259 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
1260
1261 RtlFreeAnsiString(&StringA);
1262 }
1263 else
1264 {
1265 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
1266 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
1267 }
1268 SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
1269
1270 /* Convert the computer name */
1271 RtlInitUnicodeString(&StringW, SrcPtr);
1272 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
1273 if (NT_SUCCESS(Status))
1274 {
1275 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
1276 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
1277
1278 RtlFreeAnsiString(&StringA);
1279 }
1280 else
1281 {
1282 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
1283 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
1284 }
1285
1286 /* Add the padding and the User SID */
1287 dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG));
1288 RtlZeroMemory(DstPtr, dwPadding);
1289
1290 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->UserSidOffset);
1291 DstPtr = (PVOID)((ULONG_PTR)DstPtr + dwPadding);
1292
1293 Dst->UserSidOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
1294 RtlCopyMemory(DstPtr, SrcPtr, Src->UserSidLength);
1295
1296 /* Convert the strings */
1297 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->StringOffset);
1298 DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->UserSidLength);
1299 Dst->StringOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
1300
1301 for (i = 0; i < Dst->NumStrings; i++)
1302 {
1303 RtlInitUnicodeString(&StringW, SrcPtr);
1304 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
1305 if (NT_SUCCESS(Status))
1306 {
1307 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
1308 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
1309
1310 RtlFreeAnsiString(&StringA);
1311 }
1312 else
1313 {
1314 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
1315 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
1316 }
1317 SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
1318 }
1319
1320 /* Copy the binary data */
1321 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->DataOffset);
1322 Dst->DataOffset = (ULONG_PTR)DstPtr - (ULONG_PTR)Dst;
1323 RtlCopyMemory(DstPtr, SrcPtr, Src->DataLength);
1324 DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength);
1325
1326 /* Add the padding */
1327 dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr-(ULONG_PTR)Dst) % sizeof(ULONG));
1328 RtlZeroMemory(DstPtr, dwPadding);
1329
1330 dwEntryLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst);
1331
1332 /* Set the entry length at the end of the entry */
1333 pLength = (PDWORD)((ULONG_PTR)DstPtr + dwPadding);
1334 *pLength = dwEntryLength;
1335 Dst->Length = dwEntryLength;
1336
1337 IoStatusBlock->Information = dwEntryLength;
1338
1339 Status = STATUS_SUCCESS;
1340
1341 done:
1342 if (UnicodeBuffer != NULL)
1343 RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer);
1344
1345 return Status;
1346 }
1347
1348 /*
1349 * NOTE:
1350 * 'RecordNumber' is a pointer to the record number at which the read operation
1351 * should start. If the record number is 0 and the flags given in the 'Flags'
1352 * parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is
1353 * computed.
1354 */
1355 NTSTATUS
1356 LogfReadEvents(PLOGFILE LogFile,
1357 ULONG Flags,
1358 PULONG RecordNumber,
1359 ULONG BufSize,
1360 PBYTE Buffer,
1361 PULONG BytesRead,
1362 PULONG BytesNeeded,
1363 BOOLEAN Ansi)
1364 {
1365 NTSTATUS Status;
1366 IO_STATUS_BLOCK IoStatusBlock;
1367 LARGE_INTEGER FileOffset;
1368 DWORD dwOffset, dwRead, dwRecSize;
1369 DWORD dwBufferUsage, dwRecNum;
1370
1371 /* Parameters validation */
1372
1373 /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */
1374 if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (Flags & EVENTLOG_SEEK_READ))
1375 return STATUS_INVALID_PARAMETER;
1376
1377 if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && !(Flags & EVENTLOG_SEEK_READ))
1378 return STATUS_INVALID_PARAMETER;
1379
1380 /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */
1381 if ((Flags & EVENTLOG_FORWARDS_READ) && (Flags & EVENTLOG_BACKWARDS_READ))
1382 return STATUS_INVALID_PARAMETER;
1383
1384 if (!(Flags & EVENTLOG_FORWARDS_READ) && !(Flags & EVENTLOG_BACKWARDS_READ))
1385 return STATUS_INVALID_PARAMETER;
1386
1387 if (!Buffer || !BytesRead || !BytesNeeded)
1388 return STATUS_INVALID_PARAMETER;
1389
1390 /* In seek read mode, a record number of 0 is invalid */
1391 if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
1392 return STATUS_INVALID_PARAMETER;
1393
1394 /*
1395 * In sequential read mode, a record number of 0 means we need
1396 * to determine where to start the read operation. Otherwise
1397 * we just use the provided record number.
1398 */
1399 if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
1400 {
1401 if (Flags & EVENTLOG_FORWARDS_READ)
1402 {
1403 *RecordNumber = LogFile->Header.OldestRecordNumber;
1404 }
1405 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1406 {
1407 *RecordNumber = LogFile->Header.CurrentRecordNumber - 1;
1408 }
1409 }
1410
1411 dwRecNum = *RecordNumber;
1412
1413 RtlAcquireResourceShared(&LogFile->Lock, TRUE);
1414
1415 *BytesRead = 0;
1416 *BytesNeeded = 0;
1417
1418 dwBufferUsage = 0;
1419 do
1420 {
1421 dwOffset = LogfOffsetByNumber(LogFile, dwRecNum);
1422 if (dwOffset == 0)
1423 {
1424 if (dwBufferUsage == 0)
1425 {
1426 RtlReleaseResource(&LogFile->Lock);
1427 return STATUS_END_OF_FILE;
1428 }
1429 else
1430 {
1431 break;
1432 }
1433 }
1434
1435 FileOffset.QuadPart = dwOffset;
1436 Status = NtReadFile(LogFile->hFile,
1437 NULL,
1438 NULL,
1439 NULL,
1440 &IoStatusBlock,
1441 &dwRecSize,
1442 sizeof(dwRecSize),
1443 &FileOffset,
1444 NULL);
1445 if (!NT_SUCCESS(Status))
1446 {
1447 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
1448 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1449 goto Done;
1450 }
1451 // dwRead = IoStatusBlock.Information;
1452
1453 if (dwBufferUsage + dwRecSize > BufSize)
1454 {
1455 if (dwBufferUsage == 0)
1456 {
1457 *BytesNeeded = dwRecSize;
1458 RtlReleaseResource(&LogFile->Lock);
1459 return STATUS_BUFFER_TOO_SMALL;
1460 }
1461 else
1462 {
1463 break;
1464 }
1465 }
1466
1467 FileOffset.QuadPart = dwOffset;
1468 if (Ansi)
1469 {
1470 Status = ReadAnsiLogEntry(LogFile,
1471 &IoStatusBlock,
1472 Buffer + dwBufferUsage,
1473 dwRecSize,
1474 &FileOffset,
1475 NULL);
1476 if (!NT_SUCCESS(Status))
1477 {
1478 DPRINT1("ReadAnsiLogEntry failed (Status 0x%08lx)\n", Status);
1479 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1480 goto Done;
1481 }
1482 }
1483 else
1484 {
1485 Status = ReadLogBuffer(LogFile,
1486 &IoStatusBlock,
1487 Buffer + dwBufferUsage,
1488 dwRecSize,
1489 &FileOffset,
1490 NULL);
1491 if (!NT_SUCCESS(Status))
1492 {
1493 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1494 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1495 goto Done;
1496 }
1497 }
1498 dwRead = IoStatusBlock.Information;
1499
1500 /* Go to the next event record */
1501 /*
1502 * NOTE: This implicitely supposes that all the other record numbers
1503 * are consecutive (and do not jump than more than one unit); but if
1504 * it is not the case, then we would prefer here to call some
1505 * "get_next_record_number" function.
1506 */
1507 if (Flags & EVENTLOG_FORWARDS_READ)
1508 dwRecNum++;
1509 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1510 dwRecNum--;
1511
1512 dwBufferUsage += dwRead;
1513 }
1514 while (dwBufferUsage <= BufSize);
1515
1516 *BytesRead = dwBufferUsage;
1517 *RecordNumber = dwRecNum;
1518 RtlReleaseResource(&LogFile->Lock);
1519 return STATUS_SUCCESS;
1520
1521 Done:
1522 DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status);
1523 RtlReleaseResource(&LogFile->Lock);
1524 return Status;
1525 }
1526
1527 NTSTATUS
1528 LogfWriteRecord(PLOGFILE LogFile,
1529 ULONG BufSize, // SIZE_T
1530 PEVENTLOGRECORD Record)
1531 {
1532 NTSTATUS Status;
1533 IO_STATUS_BLOCK IoStatusBlock;
1534 LARGE_INTEGER FileOffset, NextOffset;
1535 DWORD dwWritten;
1536 // DWORD dwRead;
1537 LARGE_INTEGER SystemTime;
1538 EVENTLOGEOF EofRec;
1539 EVENTLOGRECORD RecBuf;
1540 ULONG FreeSpace = 0;
1541 ULONG UpperBound;
1542 ULONG RecOffset, WriteOffset;
1543
1544 // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1545
1546 if (!Record || BufSize < sizeof(*Record))
1547 return STATUS_INVALID_PARAMETER;
1548
1549 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
1550
1551 /*
1552 * Retrieve the record written time now, that will also be compared
1553 * with the existing events timestamps in case the log is wrapping.
1554 */
1555 NtQuerySystemTime(&SystemTime);
1556 RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten);
1557
1558 Record->RecordNumber = LogFile->Header.CurrentRecordNumber;
1559
1560 /* Compute the available log free space */
1561 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1562 FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER);
1563 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1564 FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset;
1565
1566 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY;
1567
1568 /* If the event log was empty, it will now contain one record */
1569 if (LogFile->Header.OldestRecordNumber == 0)
1570 LogFile->Header.OldestRecordNumber = 1;
1571
1572 /* By default we append the new record at the old EOF record offset */
1573 WriteOffset = LogFile->Header.EndOffset;
1574
1575 /*
1576 * Check whether the log is going to wrap (the events being overwritten).
1577 */
1578
1579 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1580 UpperBound = LogFile->Header.MaxSize;
1581 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1582 UpperBound = LogFile->Header.StartOffset;
1583
1584 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1585 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1586 {
1587 DPRINT("EventLogFile has reached maximum size (%x), wrapping...\n"
1588 "UpperBound = %x, WriteOffset = %x, BufSize = %x\n",
1589 LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize);
1590 /* This will be done later */
1591 }
1592
1593 if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
1594 (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1595 {
1596 // ASSERT(UpperBound == LogFile->Header.MaxSize);
1597 // ASSERT(WriteOffset == LogFile->Header.EndOffset);
1598
1599 /*
1600 * We cannot fit the EVENTLOGRECORD header of the buffer before
1601 * the end of the file. We need to pad the end of the log with
1602 * 0x00000027, normally we will need to pad at most 0x37 bytes
1603 * (corresponding to sizeof(EVENTLOGRECORD) - 1).
1604 */
1605
1606 /* Rewind to the beginning of the log, just after the header */
1607 WriteOffset = sizeof(EVENTLOGHEADER);
1608 /**/UpperBound = LogFile->Header.StartOffset;/**/
1609
1610 FreeSpace = LogFile->Header.StartOffset - WriteOffset;
1611
1612 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1613 }
1614 /*
1615 * Otherwise, we can fit the header and only part
1616 * of the data will overwrite the oldest records.
1617 *
1618 * It might be possible that all the event record can fit in one piece,
1619 * but that the EOF record needs to be split. This is not a problem,
1620 * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
1621 */
1622
1623 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1624 {
1625 ULONG OrgOldestRecordNumber, OldestRecordNumber;
1626
1627 // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1628
1629 OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber;
1630
1631 // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
1632 // NOTE: It should be, by construction (and this should have been checked when
1633 // initializing a new, or existing log).
1634
1635 /*
1636 * Determine how many old records need to be overwritten.
1637 * Check the size of the record as the record added may be larger.
1638 * Need to take into account that we append the EOF record.
1639 */
1640 while (FreeSpace < BufSize + sizeof(EofRec))
1641 {
1642 /* Get the oldest record data */
1643 RecOffset = LogfOffsetByNumber(LogFile, OldestRecordNumber);
1644 if (RecOffset == 0)
1645 {
1646 // TODO: It cannot, queue a message box for the user and exit.
1647 // See also below...
1648 DPRINT1("Record number %d cannot be found, or LogFile is full and cannot wrap!\n", OldestRecordNumber);
1649 Status = STATUS_LOG_FILE_FULL; // STATUS_LOG_FULL;
1650 goto Quit;
1651 }
1652
1653 RtlZeroMemory(&RecBuf, sizeof(RecBuf));
1654
1655 FileOffset.QuadPart = RecOffset;
1656 Status = NtReadFile(LogFile->hFile,
1657 NULL,
1658 NULL,
1659 NULL,
1660 &IoStatusBlock,
1661 &RecBuf,
1662 sizeof(RecBuf),
1663 &FileOffset,
1664 NULL);
1665 if (!NT_SUCCESS(Status))
1666 {
1667 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
1668 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1669 goto Quit;
1670 }
1671 // dwRead = IoStatusBlock.Information;
1672
1673 if (RecBuf.Reserved != LOGFILE_SIGNATURE)
1674 {
1675 DPRINT1("LogFile corrupt!\n");
1676 Status = STATUS_EVENTLOG_FILE_CORRUPT;
1677 goto Quit;
1678 }
1679
1680 /*
1681 * Check whether this event can be overwritten by comparing its
1682 * written timestamp with the log's retention value. This value
1683 * is the time interval, in seconds, that events records are
1684 * protected from being overwritten.
1685 *
1686 * If the retention value is zero the events are always overwritten.
1687 *
1688 * If the retention value is non-zero, when the age of an event,
1689 * in seconds, reaches or exceeds this value, it can be overwritten.
1690 * Also if the events are in the future, we do not overwrite them.
1691 */
1692 if (LogFile->Header.Retention != 0 &&
1693 (Record->TimeWritten < RecBuf.TimeWritten ||
1694 (Record->TimeWritten >= RecBuf.TimeWritten &&
1695 Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention)))
1696 {
1697 // TODO: It cannot, queue a message box for the user and exit.
1698 DPRINT1("LogFile is full and cannot wrap!\n");
1699 Status = STATUS_LOG_FILE_FULL; // STATUS_LOG_FULL;
1700 goto Quit;
1701 }
1702
1703 /*
1704 * Advance the oldest record number, add the event record length
1705 * (as long as it is valid...) then take account for the possible
1706 * paddind after the record, in case this is the last one at the
1707 * end of the file.
1708 */
1709 OldestRecordNumber++;
1710 RecOffset += RecBuf.Length;
1711 FreeSpace += RecBuf.Length;
1712
1713 /*
1714 * If this was the last event record before the end of the log file,
1715 * the next one should start at the beginning of the log and the space
1716 * between the last event record and the end of the file is padded.
1717 */
1718 if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD))
1719 {
1720 /* Add the padding size */
1721 FreeSpace += LogFile->Header.MaxSize - RecOffset;
1722 }
1723 }
1724
1725 DPRINT("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize);
1726
1727 /* The log records are wrapping */
1728 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1729
1730
1731 // FIXME: May lead to corruption if the other subsequent calls fail...
1732
1733 /*
1734 * We have validated all the region of events to be discarded,
1735 * now we can perform their deletion.
1736 */
1737 LogfDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1);
1738 LogFile->Header.OldestRecordNumber = OldestRecordNumber;
1739 LogFile->Header.StartOffset = LogfOffsetByNumber(LogFile, OldestRecordNumber);
1740 if (LogFile->Header.StartOffset == 0)
1741 {
1742 /*
1743 * We have deleted all the existing event records to make place
1744 * for the new one. We can put it at the start of the event log.
1745 */
1746 LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
1747 WriteOffset = LogFile->Header.StartOffset;
1748 LogFile->Header.EndOffset = WriteOffset;
1749 }
1750
1751 DPRINT1("MaxSize = %x, StartOffset = %x, WriteOffset = %x, EndOffset = %x, BufSize = %x\n"
1752 "OldestRecordNumber = %d\n",
1753 LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize,
1754 OldestRecordNumber);
1755 }
1756
1757 /*
1758 * Expand the log file if needed.
1759 * NOTE: It may be needed to perform this task a bit sooner if we need
1760 * such a thing for performing read operations, in the future...
1761 * Or if this operation needs to modify 'FreeSpace'...
1762 */
1763 if (LogFile->CurrentSize < LogFile->Header.MaxSize)
1764 {
1765 DPRINT1("Expanding the log file from %lu to %lu\n",
1766 LogFile->CurrentSize, LogFile->Header.MaxSize);
1767
1768 /* For the moment this is a trivial operation */
1769 LogFile->CurrentSize = LogFile->Header.MaxSize;
1770 }
1771
1772 /* Pad the end of the log */
1773 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1774 if (WriteOffset < LogFile->Header.EndOffset)
1775 {
1776 /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
1777 dwWritten = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG));
1778 RtlFillMemoryUlong(&RecBuf, dwWritten, 0x00000027);
1779
1780 FileOffset.QuadPart = LogFile->Header.EndOffset;
1781 Status = NtWriteFile(LogFile->hFile,
1782 NULL,
1783 NULL,
1784 NULL,
1785 &IoStatusBlock,
1786 &RecBuf,
1787 dwWritten,
1788 &FileOffset,
1789 NULL);
1790 // dwWritten = IoStatusBlock.Information;
1791 if (!NT_SUCCESS(Status))
1792 {
1793 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
1794 // goto Quit;
1795 }
1796 }
1797
1798 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1799 FileOffset.QuadPart = WriteOffset;
1800 Status = WriteLogBuffer(LogFile,
1801 &IoStatusBlock,
1802 Record,
1803 BufSize,
1804 &FileOffset,
1805 &NextOffset);
1806 // dwWritten = IoStatusBlock.Information;
1807 if (!NT_SUCCESS(Status))
1808 {
1809 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1810 goto Quit;
1811 }
1812 /* FileOffset now contains the offset just after the end of the record buffer */
1813 FileOffset = NextOffset;
1814
1815 if (!LogfAddOffsetInformation(LogFile,
1816 Record->RecordNumber,
1817 WriteOffset))
1818 {
1819 Status = STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT;
1820 goto Quit;
1821 }
1822
1823 LogFile->Header.CurrentRecordNumber++;
1824 if (LogFile->Header.CurrentRecordNumber == 0)
1825 LogFile->Header.CurrentRecordNumber = 1;
1826
1827 /*
1828 * Write the new EOF record offset just after the event record.
1829 * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
1830 * bytes remains between the end of the record and the end of the log file.
1831 */
1832 LogFile->Header.EndOffset = FileOffset.QuadPart;
1833
1834 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
1835 EofRec.BeginRecord = LogFile->Header.StartOffset;
1836 EofRec.EndRecord = LogFile->Header.EndOffset;
1837 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1838 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
1839
1840 // FileOffset.QuadPart = LogFile->Header.EndOffset;
1841 Status = WriteLogBuffer(LogFile,
1842 &IoStatusBlock,
1843 &EofRec,
1844 sizeof(EofRec),
1845 &FileOffset,
1846 &NextOffset);
1847 // dwWritten = IoStatusBlock.Information;
1848 if (!NT_SUCCESS(Status))
1849 {
1850 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1851 goto Quit;
1852 }
1853 FileOffset = NextOffset;
1854
1855 LogFile->Header.Flags &= ELF_LOGFILE_HEADER_DIRTY;
1856
1857 /* Update the event log header */
1858 FileOffset.QuadPart = 0LL;
1859 Status = NtWriteFile(LogFile->hFile,
1860 NULL,
1861 NULL,
1862 NULL,
1863 &IoStatusBlock,
1864 &LogFile->Header,
1865 sizeof(EVENTLOGHEADER),
1866 &FileOffset,
1867 NULL);
1868 // dwWritten = IoStatusBlock.Information;
1869 if (!NT_SUCCESS(Status))
1870 {
1871 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status);
1872 goto Quit;
1873 }
1874
1875 Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock);
1876 if (!NT_SUCCESS(Status))
1877 {
1878 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status);
1879 goto Quit;
1880 }
1881
1882 Status = STATUS_SUCCESS;
1883
1884 Quit:
1885 RtlReleaseResource(&LogFile->Lock);
1886 return Status;
1887 }
1888
1889 NTSTATUS
1890 LogfClearFile(PLOGFILE LogFile,
1891 PUNICODE_STRING BackupFileName)
1892 {
1893 NTSTATUS Status;
1894
1895 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
1896
1897 if (BackupFileName->Length > 0)
1898 {
1899 /* Write a backup file */
1900 Status = LogfBackupFile(LogFile, BackupFileName);
1901 if (!NT_SUCCESS(Status))
1902 {
1903 DPRINT1("LogfBackupFile failed (Status: 0x%08lx)\n", Status);
1904 goto Quit;
1905 }
1906 }
1907
1908 Status = LogfInitializeNew(LogFile,
1909 LogFile->Header.MaxSize,
1910 LogFile->Header.Retention);
1911 if (!NT_SUCCESS(Status))
1912 {
1913 DPRINT1("LogfInitializeNew failed (Status: 0x%08lx)\n", Status);
1914 }
1915
1916 Quit:
1917 RtlReleaseResource(&LogFile->Lock);
1918 return Status;
1919 }
1920
1921 NTSTATUS
1922 LogfBackupFile(PLOGFILE LogFile,
1923 PUNICODE_STRING BackupFileName)
1924 {
1925 NTSTATUS Status;
1926 OBJECT_ATTRIBUTES ObjectAttributes;
1927 IO_STATUS_BLOCK IoStatusBlock;
1928 LARGE_INTEGER FileOffset;
1929 HANDLE FileHandle = NULL;
1930 EVENTLOGHEADER Header;
1931 EVENTLOGRECORD RecBuf;
1932 EVENTLOGEOF EofRec;
1933 ULONG i;
1934 ULONG RecOffset;
1935 PVOID Buffer = NULL;
1936
1937 // DWORD dwRead;
1938
1939 DPRINT1("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName);
1940
1941 /* Lock the log file shared */
1942 RtlAcquireResourceShared(&LogFile->Lock, TRUE);
1943
1944 InitializeObjectAttributes(&ObjectAttributes,
1945 BackupFileName,
1946 OBJ_CASE_INSENSITIVE,
1947 NULL,
1948 NULL);
1949
1950 Status = NtCreateFile(&FileHandle,
1951 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
1952 &ObjectAttributes,
1953 &IoStatusBlock,
1954 NULL,
1955 FILE_ATTRIBUTE_NORMAL,
1956 FILE_SHARE_READ,
1957 FILE_CREATE,
1958 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
1959 NULL,
1960 0);
1961 if (!NT_SUCCESS(Status))
1962 {
1963 DPRINT("Cannot create backup file %wZ (Status: 0x%08lx)\n", BackupFileName, Status);
1964 goto Done;
1965 }
1966
1967 /* Initialize the (dirty) log file header */
1968 Header.HeaderSize = sizeof(Header);
1969 Header.Signature = LOGFILE_SIGNATURE;
1970 Header.MajorVersion = MAJORVER;
1971 Header.MinorVersion = MINORVER;
1972 Header.StartOffset = sizeof(Header);
1973 Header.EndOffset = sizeof(Header);
1974 Header.CurrentRecordNumber = 1;
1975 Header.OldestRecordNumber = 0;
1976 Header.MaxSize = LogFile->Header.MaxSize;
1977 Header.Flags = ELF_LOGFILE_HEADER_DIRTY;
1978 Header.Retention = LogFile->Header.Retention;
1979 Header.EndHeaderSize = sizeof(Header);
1980
1981 /* Write the (dirty) log file header */
1982 FileOffset.QuadPart = 0LL;
1983 Status = NtWriteFile(FileHandle,
1984 NULL,
1985 NULL,
1986 NULL,
1987 &IoStatusBlock,
1988 &Header,
1989 sizeof(Header),
1990 &FileOffset,
1991 NULL);
1992 if (!NT_SUCCESS(Status))
1993 {
1994 DPRINT1("Failed to write the log file header (Status: 0x%08lx)\n", Status);
1995 goto Done;
1996 }
1997
1998 for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++)
1999 {
2000 RecOffset = LogfOffsetByNumber(LogFile, i);
2001 if (RecOffset == 0)
2002 break;
2003
2004 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
2005 FileOffset.QuadPart = RecOffset;
2006 Status = NtReadFile(LogFile->hFile,
2007 NULL,
2008 NULL,
2009 NULL,
2010 &IoStatusBlock,
2011 &RecBuf,
2012 sizeof(RecBuf),
2013 &FileOffset,
2014 NULL);
2015 if (!NT_SUCCESS(Status))
2016 {
2017 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status);
2018 goto Done;
2019 }
2020 // dwRead = IoStatusBlock.Information;
2021
2022 // if (dwRead != sizeof(RecBuf))
2023 // break;
2024
2025 Buffer = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length);
2026 if (Buffer == NULL)
2027 {
2028 DPRINT1("RtlAllocateHeap() failed!\n");
2029 goto Done;
2030 }
2031
2032 /* Read the full EVENTLOGRECORD (header + data) with wrapping */
2033 Status = ReadLogBuffer(LogFile,
2034 &IoStatusBlock,
2035 Buffer,
2036 RecBuf.Length,
2037 &FileOffset,
2038 NULL);
2039 if (!NT_SUCCESS(Status))
2040 {
2041 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
2042 RtlFreeHeap(GetProcessHeap(), 0, Buffer);
2043 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
2044 goto Done;
2045 }
2046 // dwRead = IoStatusBlock.Information;
2047
2048 /* Write the event record (no wrap for the backup log) */
2049 Status = NtWriteFile(FileHandle,
2050 NULL,
2051 NULL,
2052 NULL,
2053 &IoStatusBlock,
2054 Buffer,
2055 RecBuf.Length,
2056 NULL,
2057 NULL);
2058 if (!NT_SUCCESS(Status))
2059 {
2060 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status);
2061 RtlFreeHeap(GetProcessHeap(), 0, Buffer);
2062 goto Done;
2063 }
2064
2065 /* Update the header information */
2066 Header.EndOffset += RecBuf.Length;
2067
2068 /* Free the buffer */
2069 RtlFreeHeap(GetProcessHeap(), 0, Buffer);
2070 Buffer = NULL;
2071 }
2072
2073 /* Initialize the EOF record */
2074 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
2075 EofRec.BeginRecord = Header.StartOffset;
2076 EofRec.EndRecord = Header.EndOffset;
2077 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
2078 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
2079
2080 /* Write the EOF record (no wrap for the backup log) */
2081 Status = NtWriteFile(FileHandle,
2082 NULL,
2083 NULL,
2084 NULL,
2085 &IoStatusBlock,
2086 &EofRec,
2087 sizeof(EofRec),
2088 NULL,
2089 NULL);
2090 if (!NT_SUCCESS(Status))
2091 {
2092 DPRINT1("NtWriteFile() failed!\n");
2093 goto Done;
2094 }
2095
2096 /* Update the header information */
2097 Header.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
2098 Header.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
2099 Header.MaxSize = ROUND_UP(Header.EndOffset + sizeof(EofRec), sizeof(ULONG));
2100 Header.Flags = 0;
2101
2102 /* Write the (clean) log file header */
2103 FileOffset.QuadPart = 0LL;
2104 Status = NtWriteFile(FileHandle,
2105 NULL,
2106 NULL,
2107 NULL,
2108 &IoStatusBlock,
2109 &Header,
2110 sizeof(Header),
2111 &FileOffset,
2112 NULL);
2113 if (!NT_SUCCESS(Status))
2114 {
2115 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status);
2116 }
2117
2118 Done:
2119 /* Close the backup file */
2120 if (FileHandle != NULL)
2121 NtClose(FileHandle);
2122
2123 /* Unlock the log file */
2124 RtlReleaseResource(&LogFile->Lock);
2125
2126 return Status;
2127 }
2128
2129
2130 PEVENTLOGRECORD
2131 LogfAllocAndBuildNewRecord(PULONG lpRecSize,
2132 ULONG Time,
2133 USHORT wType,
2134 USHORT wCategory,
2135 ULONG dwEventId,
2136 PCWSTR SourceName,
2137 PCWSTR ComputerName,
2138 ULONG dwSidLength,
2139 PSID lpUserSid,
2140 USHORT wNumStrings,
2141 PWSTR lpStrings,
2142 ULONG dwDataSize,
2143 PVOID lpRawData)
2144 {
2145 DWORD dwRecSize;
2146 PEVENTLOGRECORD pRec;
2147 PWSTR str;
2148 UINT i, pos;
2149 SIZE_T SourceNameLen, ComputerNameLen, StringLen;
2150 PBYTE Buffer;
2151
2152 SourceNameLen = (SourceName ? wcslen(SourceName) : 0) + 1;
2153 ComputerNameLen = (ComputerName ? wcslen(ComputerName) : 0) + 1;
2154
2155 dwRecSize = sizeof(EVENTLOGRECORD) + (SourceNameLen + ComputerNameLen) * sizeof(WCHAR);
2156
2157 /* Align on DWORD boundary for the SID */
2158 dwRecSize = ROUND_UP(dwRecSize, sizeof(ULONG));
2159
2160 dwRecSize += dwSidLength;
2161
2162 /* Add the sizes for the strings array */
2163 ASSERT((lpStrings == NULL && wNumStrings == 0) ||
2164 (lpStrings != NULL && wNumStrings >= 0));
2165 for (i = 0, str = lpStrings; i < wNumStrings; i++)
2166 {
2167 StringLen = wcslen(str) + 1; // str must be != NULL
2168 dwRecSize += StringLen * sizeof(WCHAR);
2169 str += StringLen;
2170 }
2171
2172 /* Add the data size */
2173 dwRecSize += dwDataSize;
2174
2175 /* Align on DWORD boundary for the full structure */
2176 dwRecSize = ROUND_UP(dwRecSize, sizeof(ULONG));
2177
2178 /* Size of the trailing 'Length' member */
2179 dwRecSize += sizeof(ULONG);
2180
2181 Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRecSize);
2182 if (!Buffer)
2183 {
2184 DPRINT1("Cannot allocate heap!\n");
2185 return NULL;
2186 }
2187
2188 pRec = (PEVENTLOGRECORD)Buffer;
2189 pRec->Length = dwRecSize;
2190 pRec->Reserved = LOGFILE_SIGNATURE;
2191
2192 /*
2193 * Do not assign here any precomputed record number to the event record.
2194 * The true record number will be assigned atomically and sequentially in
2195 * LogfWriteRecord, so that all the event records will have consistent and
2196 * unique record numbers.
2197 */
2198 pRec->RecordNumber = 0;
2199
2200 /*
2201 * Set the generated time, and temporarily set the written time
2202 * with the generated time.
2203 */
2204 pRec->TimeGenerated = Time;
2205 pRec->TimeWritten = Time;
2206
2207 pRec->EventID = dwEventId;
2208 pRec->EventType = wType;
2209 pRec->EventCategory = wCategory;
2210
2211 pos = sizeof(EVENTLOGRECORD);
2212
2213 if (SourceName)
2214 StringCchCopy((PWSTR)(Buffer + pos), SourceNameLen, SourceName);
2215 pos += SourceNameLen * sizeof(WCHAR);
2216 if (ComputerName)
2217 StringCchCopy((PWSTR)(Buffer + pos), ComputerNameLen, ComputerName);
2218 pos += ComputerNameLen * sizeof(WCHAR);
2219
2220 /* Align on DWORD boundary for the SID */
2221 pos = ROUND_UP(pos, sizeof(ULONG));
2222
2223 pRec->UserSidLength = 0;
2224 pRec->UserSidOffset = 0;
2225 if (dwSidLength)
2226 {
2227 RtlCopyMemory(Buffer + pos, lpUserSid, dwSidLength);
2228 pRec->UserSidLength = dwSidLength;
2229 pRec->UserSidOffset = pos;
2230 pos += dwSidLength;
2231 }
2232
2233 pRec->StringOffset = pos;
2234 for (i = 0, str = lpStrings; i < wNumStrings; i++)
2235 {
2236 StringLen = wcslen(str) + 1; // str must be != NULL
2237 StringCchCopy((PWSTR)(Buffer + pos), StringLen, str);
2238 str += StringLen;
2239 pos += StringLen * sizeof(WCHAR);
2240 }
2241 pRec->NumStrings = wNumStrings;
2242
2243 pRec->DataLength = 0;
2244 pRec->DataOffset = 0;
2245 if (dwDataSize)
2246 {
2247 RtlCopyMemory(Buffer + pos, lpRawData, dwDataSize);
2248 pRec->DataLength = dwDataSize;
2249 pRec->DataOffset = pos;
2250 pos += dwDataSize;
2251 }
2252
2253 /* Align on DWORD boundary for the full structure */
2254 pos = ROUND_UP(pos, sizeof(ULONG));
2255
2256 /* Initialize the trailing 'Length' member */
2257 *((PDWORD) (Buffer + pos)) = dwRecSize;
2258
2259 *lpRecSize = dwRecSize;
2260 return pRec;
2261 }
2262
2263 VOID
2264 LogfReportEvent(USHORT wType,
2265 USHORT wCategory,
2266 ULONG dwEventId,
2267 USHORT wNumStrings,
2268 PWSTR lpStrings,
2269 ULONG dwDataSize,
2270 PVOID lpRawData)
2271 {
2272 NTSTATUS Status;
2273 WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
2274 DWORD dwComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
2275 PEVENTLOGRECORD logBuffer;
2276 LARGE_INTEGER SystemTime;
2277 ULONG Time;
2278 DWORD recSize;
2279
2280 if (!EventLogSource)
2281 return;
2282
2283 if (!GetComputerNameW(szComputerName, &dwComputerNameLength))
2284 {
2285 szComputerName[0] = L'\0';
2286 }
2287
2288 NtQuerySystemTime(&SystemTime);
2289 RtlTimeToSecondsSince1970(&SystemTime, &Time);
2290
2291 logBuffer = LogfAllocAndBuildNewRecord(&recSize,
2292 Time,
2293 wType,
2294 wCategory,
2295 dwEventId,
2296 EventLogSource->szName,
2297 szComputerName,
2298 0,
2299 NULL,
2300 wNumStrings,
2301 lpStrings,
2302 dwDataSize,
2303 lpRawData);
2304
2305 Status = LogfWriteRecord(EventLogSource->LogFile, recSize, logBuffer);
2306 if (!NT_SUCCESS(Status))
2307 {
2308 DPRINT1("ERROR WRITING TO EventLog %S (Status 0x%08lx)\n",
2309 EventLogSource->LogFile->FileName, Status);
2310 }
2311
2312 LogfFreeRecord(logBuffer);
2313 }