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
11 /* INCLUDES ******************************************************************/
15 #include <ndk/iofuncs.h>
16 #include <ndk/kefuncs.h>
21 /* LOG FILE LIST - GLOBALS ***************************************************/
23 static LIST_ENTRY LogFileListHead
;
24 static CRITICAL_SECTION LogFileListCs
;
26 /* LOG FILE LIST - FUNCTIONS *************************************************/
28 VOID
LogfCloseAll(VOID
)
30 EnterCriticalSection(&LogFileListCs
);
32 while (!IsListEmpty(&LogFileListHead
))
34 LogfClose(CONTAINING_RECORD(LogFileListHead
.Flink
, LOGFILE
, ListEntry
), TRUE
);
37 LeaveCriticalSection(&LogFileListCs
);
39 DeleteCriticalSection(&LogFileListCs
);
42 VOID
LogfListInitialize(VOID
)
44 InitializeCriticalSection(&LogFileListCs
);
45 InitializeListHead(&LogFileListHead
);
48 PLOGFILE
LogfListItemByName(LPCWSTR Name
)
50 PLIST_ENTRY CurrentEntry
;
51 PLOGFILE Item
, Result
= NULL
;
55 EnterCriticalSection(&LogFileListCs
);
57 CurrentEntry
= LogFileListHead
.Flink
;
58 while (CurrentEntry
!= &LogFileListHead
)
60 Item
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
62 if (Item
->LogName
&& !_wcsicmp(Item
->LogName
, Name
))
68 CurrentEntry
= CurrentEntry
->Flink
;
71 LeaveCriticalSection(&LogFileListCs
);
76 /* Index starting from 1 */
77 DWORD
LogfListItemIndexByName(LPCWSTR Name
)
79 PLIST_ENTRY CurrentEntry
;
85 EnterCriticalSection(&LogFileListCs
);
87 CurrentEntry
= LogFileListHead
.Flink
;
88 while (CurrentEntry
!= &LogFileListHead
)
90 PLOGFILE Item
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
92 if (Item
->LogName
&& !_wcsicmp(Item
->LogName
, Name
))
98 CurrentEntry
= CurrentEntry
->Flink
;
102 LeaveCriticalSection(&LogFileListCs
);
107 /* Index starting from 1 */
108 PLOGFILE
LogfListItemByIndex(DWORD Index
)
110 PLIST_ENTRY CurrentEntry
;
111 PLOGFILE Result
= NULL
;
114 EnterCriticalSection(&LogFileListCs
);
116 CurrentEntry
= LogFileListHead
.Flink
;
117 while (CurrentEntry
!= &LogFileListHead
)
121 Result
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
125 CurrentEntry
= CurrentEntry
->Flink
;
129 LeaveCriticalSection(&LogFileListCs
);
133 DWORD
LogfListItemCount(VOID
)
135 PLIST_ENTRY CurrentEntry
;
138 EnterCriticalSection(&LogFileListCs
);
140 CurrentEntry
= LogFileListHead
.Flink
;
141 while (CurrentEntry
!= &LogFileListHead
)
143 CurrentEntry
= CurrentEntry
->Flink
;
147 LeaveCriticalSection(&LogFileListCs
);
152 LogfListAddItem(PLOGFILE Item
)
154 EnterCriticalSection(&LogFileListCs
);
155 InsertTailList(&LogFileListHead
, &Item
->ListEntry
);
156 LeaveCriticalSection(&LogFileListCs
);
160 LogfListRemoveItem(PLOGFILE Item
)
162 EnterCriticalSection(&LogFileListCs
);
163 RemoveEntryList(&Item
->ListEntry
);
164 LeaveCriticalSection(&LogFileListCs
);
168 /* GLOBALS *******************************************************************/
170 static const EVENTLOGEOF EOFRecord
=
173 0x11111111, 0x22222222, 0x33333333, 0x44444444,
178 /* FUNCTIONS *****************************************************************/
181 ReadLogBuffer(IN PLOGFILE LogFile
,
182 OUT PIO_STATUS_BLOCK IoStatusBlock
,
185 IN PLARGE_INTEGER ByteOffset
,
186 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
190 LARGE_INTEGER FileOffset
;
192 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
193 // ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize);
194 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
197 NextOffset
->QuadPart
= 0LL;
199 /* Read the first part of the buffer */
200 FileOffset
= *ByteOffset
;
201 BufSize
= min(Length
, LogFile
->CurrentSize
- FileOffset
.QuadPart
);
203 Status
= NtReadFile(LogFile
->hFile
,
212 if (!NT_SUCCESS(Status
))
214 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
218 if (Length
> BufSize
)
220 ULONG_PTR Information
= IoStatusBlock
->Information
;
223 * The buffer was splitted in two, its second part
224 * is to be read at the beginning of the log.
226 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
227 BufSize
= Length
- BufSize
;
228 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
230 Status
= NtReadFile(LogFile
->hFile
,
239 if (!NT_SUCCESS(Status
))
241 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
244 /* Add the read number of bytes from the first read */
245 IoStatusBlock
->Information
+= Information
;
248 /* We return the offset just after the end of the read buffer */
250 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
256 WriteLogBuffer(IN PLOGFILE LogFile
,
257 OUT PIO_STATUS_BLOCK IoStatusBlock
,
260 IN PLARGE_INTEGER ByteOffset
,
261 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
265 LARGE_INTEGER FileOffset
;
267 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
268 ASSERT(ByteOffset
->QuadPart
<= LogFile
->Header
.MaxSize
);
269 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
272 NextOffset
->QuadPart
= 0LL;
274 /* Write the first part of the buffer */
275 FileOffset
= *ByteOffset
;
276 BufSize
= min(Length
, LogFile
->CurrentSize
/* LogFile->Header.MaxSize */ - FileOffset
.QuadPart
);
278 Status
= NtWriteFile(LogFile
->hFile
,
287 if (!NT_SUCCESS(Status
))
289 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
293 if (Length
> BufSize
)
295 ULONG_PTR Information
= IoStatusBlock
->Information
;
298 * The buffer was splitted in two, its second part is written
299 * at the beginning of the log.
301 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
302 BufSize
= Length
- BufSize
;
303 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
305 Status
= NtWriteFile(LogFile
->hFile
,
314 if (!NT_SUCCESS(Status
))
316 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
319 /* Add the written number of bytes from the first write */
320 IoStatusBlock
->Information
+= Information
;
323 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
326 /* We return the offset just after the end of the written buffer */
328 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
334 /* Returns 0 if nothing is found */
336 LogfOffsetByNumber(PLOGFILE LogFile
,
341 for (i
= 0; i
< LogFile
->OffsetInfoNext
; i
++)
343 if (LogFile
->OffsetInfo
[i
].EventNumber
== RecordNumber
)
344 return LogFile
->OffsetInfo
[i
].EventOffset
;
350 LogfAddOffsetInformation(PLOGFILE LogFile
,
356 if (LogFile
->OffsetInfoNext
== LogFile
->OffsetInfoSize
)
358 NewOffsetInfo
= HeapReAlloc(GetProcessHeap(),
361 (LogFile
->OffsetInfoSize
+ 64) *
362 sizeof(EVENT_OFFSET_INFO
));
366 DPRINT1("Cannot reallocate heap.\n");
370 LogFile
->OffsetInfo
= (PEVENT_OFFSET_INFO
)NewOffsetInfo
;
371 LogFile
->OffsetInfoSize
+= 64;
374 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventNumber
= ulNumber
;
375 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventOffset
= ulOffset
;
376 LogFile
->OffsetInfoNext
++;
382 LogfDeleteOffsetInformation(PLOGFILE LogFile
,
388 if (ulNumberMin
> ulNumberMax
)
391 /* Remove records ulNumberMin to ulNumberMax inclusive */
392 while (ulNumberMin
<= ulNumberMax
)
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.
399 if (ulNumberMin
!= LogFile
->OffsetInfo
[0].EventNumber
)
403 * RtlMoveMemory(&LogFile->OffsetInfo[0],
404 * &LogFile->OffsetInfo[1],
405 * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
407 for (i
= 0; i
< LogFile
->OffsetInfoNext
- 1; i
++)
409 LogFile
->OffsetInfo
[i
].EventNumber
= LogFile
->OffsetInfo
[i
+ 1].EventNumber
;
410 LogFile
->OffsetInfo
[i
].EventOffset
= LogFile
->OffsetInfo
[i
+ 1].EventOffset
;
412 LogFile
->OffsetInfoNext
--;
414 /* Go to the next offset information */
422 LogfInitializeNew(PLOGFILE LogFile
,
427 IO_STATUS_BLOCK IoStatusBlock
;
428 LARGE_INTEGER FileOffset
;
431 /* Initialize the event log header */
432 RtlZeroMemory(&LogFile
->Header
, sizeof(EVENTLOGHEADER
));
434 LogFile
->Header
.HeaderSize
= sizeof(EVENTLOGHEADER
);
435 LogFile
->Header
.Signature
= LOGFILE_SIGNATURE
;
436 LogFile
->Header
.MajorVersion
= MAJORVER
;
437 LogFile
->Header
.MinorVersion
= MINORVER
;
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;
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.
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
;
455 LogFile
->Header
.Flags
= 0;
456 LogFile
->Header
.Retention
= ulRetention
;
457 LogFile
->Header
.EndHeaderSize
= sizeof(EVENTLOGHEADER
);
459 /* Write the header */
460 SetFilePointer(LogFile
->hFile
, 0, NULL
, FILE_BEGIN
);
461 SetEndOfFile(LogFile
->hFile
);
463 FileOffset
.QuadPart
= 0LL;
464 Status
= NtWriteFile(LogFile
->hFile
,
470 sizeof(EVENTLOGHEADER
),
473 if (!NT_SUCCESS(Status
))
475 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
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
;
486 Status
= NtWriteFile(LogFile
->hFile
,
495 if (!NT_SUCCESS(Status
))
497 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
501 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
502 if (!NT_SUCCESS(Status
))
504 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
508 return STATUS_SUCCESS
;
512 LogfInitializeExisting(PLOGFILE LogFile
,
516 IO_STATUS_BLOCK IoStatusBlock
;
517 LARGE_INTEGER FileOffset
, NextOffset
;
518 LARGE_INTEGER LogFileSize
;
519 DWORD dwRecordsNumber
= 0;
523 EVENTLOGRECORD RecBuf
;
524 PEVENTLOGRECORD pRecBuf
;
525 BOOLEAN Wrapping
= FALSE
;
526 BOOLEAN IsLogDirty
= FALSE
;
528 DPRINT("Initializing LogFile %S\n", LogFile
->LogName
);
530 /* Read the log header */
531 FileOffset
.QuadPart
= 0LL;
532 Status
= NtReadFile(LogFile
->hFile
,
538 sizeof(EVENTLOGHEADER
),
541 if (!NT_SUCCESS(Status
))
543 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
544 return STATUS_EVENTLOG_FILE_CORRUPT
;
546 if (IoStatusBlock
.Information
!= sizeof(EVENTLOGHEADER
))
548 DPRINT("EventLog: Invalid file %S.\n", LogFile
->FileName
);
549 return STATUS_EVENTLOG_FILE_CORRUPT
;
552 /* Header validity checks */
554 if (LogFile
->Header
.HeaderSize
!= sizeof(EVENTLOGHEADER
) ||
555 LogFile
->Header
.EndHeaderSize
!= sizeof(EVENTLOGHEADER
))
557 DPRINT("EventLog: Invalid header size in %S.\n", LogFile
->FileName
);
558 return STATUS_EVENTLOG_FILE_CORRUPT
;
561 if (LogFile
->Header
.Signature
!= LOGFILE_SIGNATURE
)
563 DPRINT("EventLog: Invalid signature %x in %S.\n",
564 LogFile
->Header
.Signature
, LogFile
->FileName
);
565 return STATUS_EVENTLOG_FILE_CORRUPT
;
568 IsLogDirty
= (LogFile
->Header
.Flags
& ELF_LOGFILE_HEADER_DIRTY
);
570 /* If the log is a backup log that is dirty, then it is corrupted */
571 if (Backup
&& IsLogDirty
)
573 DPRINT("EventLog: Backup log %S is dirty.\n", LogFile
->FileName
);
574 return STATUS_EVENTLOG_FILE_CORRUPT
;
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 .
581 if (!GetFileSizeEx(LogFile
->hFile
, &LogFileSize
))
582 return I_RpcMapWin32Status(GetLastError());
584 if (LogFileSize
.HighPart
!= 0)
586 DPRINT1("EventLog: Log %S is too large.\n", LogFile
->FileName
);
587 // return STATUS_FILE_TOO_LARGE;
588 return STATUS_EVENTLOG_FILE_CORRUPT
;
591 LogFile
->CurrentSize
= LogFileSize
.LowPart
; // LogFileSize.QuadPart;
593 /* Adjust the log maximum size if needed */
594 if (LogFile
->CurrentSize
> LogFile
->Header
.MaxSize
)
595 LogFile
->Header
.MaxSize
= LogFile
->CurrentSize
;
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.
604 if ((LogFile
->Header
.EndOffset
>= sizeof(EVENTLOGHEADER
)) &&
605 (LogFile
->Header
.EndOffset
< LogFile
->CurrentSize
) &&
606 (LogFile
->Header
.EndOffset
& 3) == 0) // EndOffset % sizeof(ULONG) == 0
608 /* The header EOF offset may be valid, try to start with it */
609 RecOffset
= LogFile
->Header
.EndOffset
;
613 /* The header EOF offset could not be valid, so start from the beginning */
614 RecOffset
= sizeof(EVENTLOGHEADER
);
617 FileOffset
.QuadPart
= RecOffset
;
622 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
624 DPRINT1("EOF record not found!\n");
625 return STATUS_EVENTLOG_FILE_CORRUPT
;
628 /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
629 Status
= ReadLogBuffer(LogFile
,
632 EVENTLOGEOF_SIZE_FIXED
,
635 if (!NT_SUCCESS(Status
))
637 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
638 return STATUS_EVENTLOG_FILE_CORRUPT
;
640 if (IoStatusBlock
.Information
!= EVENTLOGEOF_SIZE_FIXED
)
642 DPRINT1("Cannot read at most an EOF record!\n");
643 return STATUS_EVENTLOG_FILE_CORRUPT
;
646 /* Is it an EVENTLOGEOF record? */
647 if (RtlCompareMemory(&EofRec
, &EOFRecord
, EVENTLOGEOF_SIZE_FIXED
) == EVENTLOGEOF_SIZE_FIXED
)
649 DPRINT("Found EOF record at %llx\n", FileOffset
.QuadPart
);
651 /* Got it! Break the loop and continue */
655 /* No, continue looping */
656 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
657 FileOffset
.QuadPart
+= sizeof(ULONG
);
659 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 2*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
660 FileOffset
.QuadPart
+= 2*sizeof(ULONG
);
662 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 3*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
663 FileOffset
.QuadPart
+= 3*sizeof(ULONG
);
665 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 4*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
666 FileOffset
.QuadPart
+= 4*sizeof(ULONG
);
668 FileOffset
.QuadPart
+= 5*sizeof(ULONG
); // EVENTLOGEOF_SIZE_FIXED
670 if (FileOffset
.QuadPart
>= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */)
672 /* Wrap the offset */
673 FileOffset
.QuadPart
-= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER
);
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
683 /* Read the full EVENTLOGEOF (may wrap) and validate it */
684 Status
= ReadLogBuffer(LogFile
,
690 if (!NT_SUCCESS(Status
))
692 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
693 return STATUS_EVENTLOG_FILE_CORRUPT
;
695 if (IoStatusBlock
.Information
!= sizeof(EofRec
))
697 DPRINT1("Cannot read the full EOF record!\n");
698 return STATUS_EVENTLOG_FILE_CORRUPT
;
701 /* Complete validity checks */
702 if ((EofRec
.RecordSizeEnd
!= EofRec
.RecordSizeBeginning
) ||
703 (EofRec
.EndRecord
!= FileOffset
.QuadPart
))
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
;
715 /* The EOF record is valid, break the loop and continue */
717 /* If the log is not dirty, the header values should correspond to the EOF ones */
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
) )
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
);
734 return STATUS_EVENTLOG_FILE_CORRUPT
;
738 /* If the log is dirty, patch the log header with the values from the EOF record */
739 if (!Backup
&& IsLogDirty
)
741 LogFile
->Header
.StartOffset
= EofRec
.BeginRecord
;
742 LogFile
->Header
.EndOffset
= EofRec
.EndRecord
;
743 LogFile
->Header
.CurrentRecordNumber
= EofRec
.CurrentRecordNumber
;
744 LogFile
->Header
.OldestRecordNumber
= EofRec
.OldestRecordNumber
;
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.
755 /* Verify Start/End offsets boundaries */
757 if ((LogFile
->Header
.StartOffset
>= LogFile
->CurrentSize
) ||
758 (LogFile
->Header
.StartOffset
& 3) != 0) // StartOffset % sizeof(ULONG) != 0
760 DPRINT("EventLog: Invalid start offset %x in %S.\n",
761 LogFile
->Header
.StartOffset
, LogFile
->FileName
);
762 return STATUS_EVENTLOG_FILE_CORRUPT
;
764 if ((LogFile
->Header
.EndOffset
>= LogFile
->CurrentSize
) ||
765 (LogFile
->Header
.EndOffset
& 3) != 0) // EndOffset % sizeof(ULONG) != 0
767 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
768 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
769 return STATUS_EVENTLOG_FILE_CORRUPT
;
772 if ((LogFile
->Header
.StartOffset
!= LogFile
->Header
.EndOffset
) &&
773 (LogFile
->Header
.MaxSize
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
776 * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
777 * it should point to a non-splitted EVENTLOGRECORD.
779 DPRINT("EventLog: Invalid start offset %x in %S.\n",
780 LogFile
->Header
.StartOffset
, LogFile
->FileName
);
781 return STATUS_EVENTLOG_FILE_CORRUPT
;
784 if ((LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
785 (LogFile
->Header
.EndOffset
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
788 * In non-wrapping case, there must be enough space between StartOffset
789 * and EndOffset to contain at least a full EVENTLOGRECORD.
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
;
796 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
799 * Non-wrapping case: the (wrapping) free space starting at EndOffset
800 * must be able to contain an EVENTLOGEOF.
802 if (LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
+
803 LogFile
->Header
.StartOffset
- sizeof(EVENTLOGHEADER
) < sizeof(EVENTLOGEOF
))
805 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
806 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
807 return STATUS_EVENTLOG_FILE_CORRUPT
;
810 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
813 * Wrapping case: the free space between EndOffset and StartOffset
814 * must be able to contain an EVENTLOGEOF.
816 if (LogFile
->Header
.StartOffset
- LogFile
->Header
.EndOffset
< sizeof(EVENTLOGEOF
))
818 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
819 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
820 return STATUS_EVENTLOG_FILE_CORRUPT
;
824 /* Start enumerating the event records from the beginning */
825 RecOffset
= LogFile
->Header
.StartOffset
;
826 FileOffset
.QuadPart
= RecOffset
;
830 // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
832 // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
833 // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
836 DPRINT("StartOffset = %x, EndOffset = %x\n",
837 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
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.
846 pRecBuf
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
849 DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
858 DPRINT("StartOffset = %x, EndOffset = %x\n",
859 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
862 while (FileOffset
.QuadPart
!= LogFile
->Header
.EndOffset
)
864 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
866 /* We have finished enumerating all the event records */
870 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
871 Status
= NtReadFile(LogFile
->hFile
,
880 if (!NT_SUCCESS(Status
))
882 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
883 return STATUS_EVENTLOG_FILE_CORRUPT
;
885 if (IoStatusBlock
.Information
!= sizeof(RecBuf
))
887 DPRINT("Length != sizeof(RecBuf)\n");
891 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
||
892 RecBuf
.Length
< sizeof(EVENTLOGRECORD
))
894 DPRINT("RecBuf problem\n");
898 /* Allocate a full EVENTLOGRECORD (header + data) */
899 pRecBuf
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
902 DPRINT1("Cannot allocate heap!\n");
903 return STATUS_NO_MEMORY
;
906 /* Attempt to read the full EVENTLOGRECORD (can wrap) */
907 Status
= ReadLogBuffer(LogFile
,
913 if (!NT_SUCCESS(Status
))
915 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
916 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
917 return STATUS_EVENTLOG_FILE_CORRUPT
;
919 if (IoStatusBlock
.Information
!= RecBuf
.Length
)
921 DPRINT1("Oh oh!!\n");
922 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
926 // /* If OverWrittenRecords is TRUE and this record has already been read */
927 // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
929 // RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
933 pdwRecSize2
= (PDWORD
)((ULONG_PTR
)pRecBuf
+ RecBuf
.Length
- 4);
935 if (*pdwRecSize2
!= RecBuf
.Length
)
937 DPRINT1("Invalid RecordSizeEnd of record %d (%x) in %S\n",
938 dwRecordsNumber
, *pdwRecSize2
, LogFile
->LogName
);
939 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
943 DPRINT("Add new record %d - %x\n", pRecBuf
->RecordNumber
, FileOffset
.QuadPart
);
947 if (!LogfAddOffsetInformation(LogFile
,
948 pRecBuf
->RecordNumber
,
949 FileOffset
.QuadPart
))
951 DPRINT1("LogfAddOffsetInformation() failed!\n");
952 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
953 return STATUS_EVENTLOG_FILE_CORRUPT
;
956 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
958 if (NextOffset
.QuadPart
== LogFile
->Header
.EndOffset
)
960 /* We have finished enumerating all the event records */
961 DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
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.
970 if (LogFile
->Header
.MaxSize
- NextOffset
.QuadPart
< sizeof(EVENTLOGRECORD
))
972 /* Wrap to the beginning of the log */
974 NextOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
978 * If the next offset to read is below the current offset,
979 * this means we are wrapping.
981 if (FileOffset
.QuadPart
> NextOffset
.QuadPart
)
983 DPRINT("Wrapping = TRUE;\n");
987 /* Move the current offset */
988 FileOffset
= NextOffset
;
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;
995 LogFile
->Header
.CurrentRecordNumber
= dwRecordsNumber
+ LogFile
->Header
.OldestRecordNumber
;
996 if (LogFile
->Header
.CurrentRecordNumber
== 0)
997 LogFile
->Header
.CurrentRecordNumber
= 1;
1001 FileOffset
.QuadPart
= 0LL;
1002 Status
= NtWriteFile(LogFile
->hFile
,
1008 sizeof(EVENTLOGHEADER
),
1011 if (!NT_SUCCESS(Status
))
1013 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1014 return STATUS_EVENTLOG_FILE_CORRUPT
;
1017 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1018 if (!NT_SUCCESS(Status
))
1020 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
1021 return STATUS_EVENTLOG_FILE_CORRUPT
;
1025 return STATUS_SUCCESS
;
1029 LogfCreate(PLOGFILE
* LogFile
,
1031 PUNICODE_STRING FileName
,
1037 NTSTATUS Status
= STATUS_SUCCESS
;
1038 OBJECT_ATTRIBUTES ObjectAttributes
;
1039 IO_STATUS_BLOCK IoStatusBlock
;
1042 BOOLEAN CreateNew
= FALSE
;
1044 pLogFile
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LOGFILE
));
1047 DPRINT1("Cannot allocate heap!\n");
1048 return STATUS_NO_MEMORY
;
1051 InitializeObjectAttributes(&ObjectAttributes
,
1053 OBJ_CASE_INSENSITIVE
,
1057 Status
= NtCreateFile(&pLogFile
->hFile
,
1058 Backup
? (GENERIC_READ
| SYNCHRONIZE
)
1059 : (GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
),
1063 FILE_ATTRIBUTE_NORMAL
,
1065 Backup
? FILE_OPEN
: FILE_OPEN_IF
,
1066 FILE_SYNCHRONOUS_IO_NONALERT
,
1069 if (!NT_SUCCESS(Status
))
1071 DPRINT1("Cannot create file %wZ (Status: 0x%08lx)\n", FileName
, Status
);
1075 CreateNew
= (IoStatusBlock
.Information
== FILE_CREATED
);
1077 LogNameLen
= (LogName
? wcslen(LogName
) : 0) + 1;
1078 pLogFile
->LogName
= RtlAllocateHeap(GetProcessHeap(),
1080 LogNameLen
* sizeof(WCHAR
));
1081 if (pLogFile
->LogName
== NULL
)
1083 DPRINT1("Cannot allocate heap\n");
1084 Status
= STATUS_NO_MEMORY
;
1089 StringCchCopy(pLogFile
->LogName
, LogNameLen
, LogName
);
1091 pLogFile
->FileName
= RtlAllocateHeap(GetProcessHeap(),
1093 /*(wcslen(FileName->Buffer) + 1) * sizeof(WCHAR)*/
1094 FileName
->Length
+ sizeof(UNICODE_NULL
));
1095 if (pLogFile
->FileName
== NULL
)
1097 DPRINT1("Cannot allocate heap\n");
1098 Status
= STATUS_NO_MEMORY
;
1102 StringCchCopy(pLogFile
->FileName
,
1103 /*wcslen(FileName->Buffer) + 1*/ (FileName
->Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
),
1106 pLogFile
->OffsetInfo
= RtlAllocateHeap(GetProcessHeap(),
1108 sizeof(EVENT_OFFSET_INFO
) * 64);
1109 if (pLogFile
->OffsetInfo
== NULL
)
1111 DPRINT1("Cannot allocate heap\n");
1112 Status
= STATUS_NO_MEMORY
;
1115 pLogFile
->OffsetInfoSize
= 64;
1116 pLogFile
->OffsetInfoNext
= 0;
1118 pLogFile
->Permanent
= Permanent
;
1120 // FIXME: Always use the regitry values for MaxSize & Retention,
1121 // even for existing logs!
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.
1128 Status
= LogfInitializeNew(pLogFile
, ulMaxSize
, ulRetention
);
1130 Status
= LogfInitializeExisting(pLogFile
, Backup
);
1132 if (!NT_SUCCESS(Status
))
1135 RtlInitializeResource(&pLogFile
->Lock
);
1137 LogfListAddItem(pLogFile
);
1140 if (!NT_SUCCESS(Status
))
1142 if ((pLogFile
->hFile
!= NULL
) && (pLogFile
->hFile
!= INVALID_HANDLE_VALUE
))
1143 NtClose(pLogFile
->hFile
);
1145 if (pLogFile
->OffsetInfo
)
1146 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->OffsetInfo
);
1148 if (pLogFile
->FileName
)
1149 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->FileName
);
1151 if (pLogFile
->LogName
)
1152 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->LogName
);
1154 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
);
1158 *LogFile
= pLogFile
;
1165 LogfClose(PLOGFILE LogFile
,
1168 IO_STATUS_BLOCK IoStatusBlock
;
1170 if (LogFile
== NULL
)
1173 if (!ForceClose
&& LogFile
->Permanent
)
1176 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1178 NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1179 NtClose(LogFile
->hFile
);
1180 LogfListRemoveItem(LogFile
);
1182 RtlDeleteResource(&LogFile
->Lock
);
1184 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->LogName
);
1185 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->FileName
);
1186 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->OffsetInfo
);
1187 RtlFreeHeap(GetProcessHeap(), 0, LogFile
);
1194 ReadAnsiLogEntry(IN PLOGFILE LogFile
,
1195 OUT PIO_STATUS_BLOCK IoStatusBlock
,
1198 IN PLARGE_INTEGER ByteOffset
,
1199 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
1202 PVOID UnicodeBuffer
= NULL
;
1203 PEVENTLOGRECORD Src
, Dst
;
1204 ANSI_STRING StringA
;
1205 UNICODE_STRING StringW
;
1206 PVOID SrcPtr
, DstPtr
;
1210 DWORD dwEntryLength
;
1213 IoStatusBlock
->Information
= 0;
1215 UnicodeBuffer
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, Length
);
1216 if (UnicodeBuffer
== NULL
)
1218 DPRINT1("Alloc failed!\n");
1219 return STATUS_NO_MEMORY
;
1222 Status
= ReadLogBuffer(LogFile
,
1228 if (!NT_SUCCESS(Status
))
1230 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1231 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1234 // dwRead = IoStatusBlock->Information;
1236 Src
= (PEVENTLOGRECORD
)UnicodeBuffer
;
1237 Dst
= (PEVENTLOGRECORD
)Buffer
;
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
;
1250 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ sizeof(EVENTLOGRECORD
));
1251 DstPtr
= (PVOID
)((ULONG_PTR
)Dst
+ sizeof(EVENTLOGRECORD
));
1253 /* Convert the module name */
1254 RtlInitUnicodeString(&StringW
, SrcPtr
);
1255 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1256 if (NT_SUCCESS(Status
))
1258 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1259 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1261 RtlFreeAnsiString(&StringA
);
1265 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1266 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1268 SrcPtr
= (PVOID
)((ULONG_PTR
)SrcPtr
+ StringW
.MaximumLength
);
1270 /* Convert the computer name */
1271 RtlInitUnicodeString(&StringW
, SrcPtr
);
1272 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1273 if (NT_SUCCESS(Status
))
1275 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1276 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1278 RtlFreeAnsiString(&StringA
);
1282 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1283 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1286 /* Add the padding and the User SID */
1287 dwPadding
= sizeof(ULONG
) - (((ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
) % sizeof(ULONG
));
1288 RtlZeroMemory(DstPtr
, dwPadding
);
1290 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ Src
->UserSidOffset
);
1291 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ dwPadding
);
1293 Dst
->UserSidOffset
= (DWORD
)((ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
);
1294 RtlCopyMemory(DstPtr
, SrcPtr
, Src
->UserSidLength
);
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
);
1301 for (i
= 0; i
< Dst
->NumStrings
; i
++)
1303 RtlInitUnicodeString(&StringW
, SrcPtr
);
1304 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1305 if (NT_SUCCESS(Status
))
1307 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1308 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1310 RtlFreeAnsiString(&StringA
);
1314 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1315 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1317 SrcPtr
= (PVOID
)((ULONG_PTR
)SrcPtr
+ StringW
.MaximumLength
);
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
);
1326 /* Add the padding */
1327 dwPadding
= sizeof(ULONG
) - (((ULONG_PTR
)DstPtr
-(ULONG_PTR
)Dst
) % sizeof(ULONG
));
1328 RtlZeroMemory(DstPtr
, dwPadding
);
1330 dwEntryLength
= (DWORD
)((ULONG_PTR
)DstPtr
+ dwPadding
+ sizeof(ULONG
) - (ULONG_PTR
)Dst
);
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
;
1337 IoStatusBlock
->Information
= dwEntryLength
;
1339 Status
= STATUS_SUCCESS
;
1342 if (UnicodeBuffer
!= NULL
)
1343 RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer
);
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
1356 LogfReadEvents(PLOGFILE LogFile
,
1358 PULONG RecordNumber
,
1366 IO_STATUS_BLOCK IoStatusBlock
;
1367 LARGE_INTEGER FileOffset
;
1368 DWORD dwOffset
, dwRead
, dwRecSize
;
1369 DWORD dwBufferUsage
, dwRecNum
;
1371 /* Parameters validation */
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
;
1377 if (!(Flags
& EVENTLOG_SEQUENTIAL_READ
) && !(Flags
& EVENTLOG_SEEK_READ
))
1378 return STATUS_INVALID_PARAMETER
;
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
;
1384 if (!(Flags
& EVENTLOG_FORWARDS_READ
) && !(Flags
& EVENTLOG_BACKWARDS_READ
))
1385 return STATUS_INVALID_PARAMETER
;
1387 if (!Buffer
|| !BytesRead
|| !BytesNeeded
)
1388 return STATUS_INVALID_PARAMETER
;
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
;
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.
1399 if ((Flags
& EVENTLOG_SEQUENTIAL_READ
) && (*RecordNumber
== 0))
1401 if (Flags
& EVENTLOG_FORWARDS_READ
)
1403 *RecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1405 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1407 *RecordNumber
= LogFile
->Header
.CurrentRecordNumber
- 1;
1411 dwRecNum
= *RecordNumber
;
1413 RtlAcquireResourceShared(&LogFile
->Lock
, TRUE
);
1421 dwOffset
= LogfOffsetByNumber(LogFile
, dwRecNum
);
1424 if (dwBufferUsage
== 0)
1426 RtlReleaseResource(&LogFile
->Lock
);
1427 return STATUS_END_OF_FILE
;
1435 FileOffset
.QuadPart
= dwOffset
;
1436 Status
= NtReadFile(LogFile
->hFile
,
1445 if (!NT_SUCCESS(Status
))
1447 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
1448 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1451 // dwRead = IoStatusBlock.Information;
1453 if (dwBufferUsage
+ dwRecSize
> BufSize
)
1455 if (dwBufferUsage
== 0)
1457 *BytesNeeded
= dwRecSize
;
1458 RtlReleaseResource(&LogFile
->Lock
);
1459 return STATUS_BUFFER_TOO_SMALL
;
1467 FileOffset
.QuadPart
= dwOffset
;
1470 Status
= ReadAnsiLogEntry(LogFile
,
1472 Buffer
+ dwBufferUsage
,
1476 if (!NT_SUCCESS(Status
))
1478 DPRINT1("ReadAnsiLogEntry failed (Status 0x%08lx)\n", Status
);
1479 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1485 Status
= ReadLogBuffer(LogFile
,
1487 Buffer
+ dwBufferUsage
,
1491 if (!NT_SUCCESS(Status
))
1493 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1494 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1498 dwRead
= IoStatusBlock
.Information
;
1500 /* Go to the next event record */
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.
1507 if (Flags
& EVENTLOG_FORWARDS_READ
)
1509 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1512 dwBufferUsage
+= dwRead
;
1514 while (dwBufferUsage
<= BufSize
);
1516 *BytesRead
= dwBufferUsage
;
1517 *RecordNumber
= dwRecNum
;
1518 RtlReleaseResource(&LogFile
->Lock
);
1519 return STATUS_SUCCESS
;
1522 DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status
);
1523 RtlReleaseResource(&LogFile
->Lock
);
1528 LogfWriteRecord(PLOGFILE LogFile
,
1529 ULONG BufSize
, // SIZE_T
1530 PEVENTLOGRECORD Record
)
1533 IO_STATUS_BLOCK IoStatusBlock
;
1534 LARGE_INTEGER FileOffset
, NextOffset
;
1537 LARGE_INTEGER SystemTime
;
1539 EVENTLOGRECORD RecBuf
;
1540 ULONG FreeSpace
= 0;
1542 ULONG RecOffset
, WriteOffset
;
1544 // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1546 if (!Record
|| BufSize
< sizeof(*Record
))
1547 return STATUS_INVALID_PARAMETER
;
1549 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1552 * Retrieve the record written time now, that will also be compared
1553 * with the existing events timestamps in case the log is wrapping.
1555 NtQuerySystemTime(&SystemTime
);
1556 RtlTimeToSecondsSince1970(&SystemTime
, &Record
->TimeWritten
);
1558 Record
->RecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
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
;
1566 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_DIRTY
;
1568 /* If the event log was empty, it will now contain one record */
1569 if (LogFile
->Header
.OldestRecordNumber
== 0)
1570 LogFile
->Header
.OldestRecordNumber
= 1;
1572 /* By default we append the new record at the old EOF record offset */
1573 WriteOffset
= LogFile
->Header
.EndOffset
;
1576 * Check whether the log is going to wrap (the events being overwritten).
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
;
1584 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1585 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
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 */
1593 if ( (LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
1594 (LogFile
->Header
.MaxSize
- WriteOffset
< sizeof(RecBuf
)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1596 // ASSERT(UpperBound == LogFile->Header.MaxSize);
1597 // ASSERT(WriteOffset == LogFile->Header.EndOffset);
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).
1606 /* Rewind to the beginning of the log, just after the header */
1607 WriteOffset
= sizeof(EVENTLOGHEADER
);
1608 /**/UpperBound
= LogFile
->Header
.StartOffset
;/**/
1610 FreeSpace
= LogFile
->Header
.StartOffset
- WriteOffset
;
1612 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1615 * Otherwise, we can fit the header and only part
1616 * of the data will overwrite the oldest records.
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.
1623 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
1625 ULONG OrgOldestRecordNumber
, OldestRecordNumber
;
1627 // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1629 OldestRecordNumber
= OrgOldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
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).
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.
1640 while (FreeSpace
< BufSize
+ sizeof(EofRec
))
1642 /* Get the oldest record data */
1643 RecOffset
= LogfOffsetByNumber(LogFile
, OldestRecordNumber
);
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;
1653 RtlZeroMemory(&RecBuf
, sizeof(RecBuf
));
1655 FileOffset
.QuadPart
= RecOffset
;
1656 Status
= NtReadFile(LogFile
->hFile
,
1665 if (!NT_SUCCESS(Status
))
1667 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
1668 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1671 // dwRead = IoStatusBlock.Information;
1673 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
)
1675 DPRINT1("LogFile corrupt!\n");
1676 Status
= STATUS_EVENTLOG_FILE_CORRUPT
;
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.
1686 * If the retention value is zero the events are always overwritten.
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.
1692 if (LogFile
->Header
.Retention
!= 0 &&
1693 (Record
->TimeWritten
< RecBuf
.TimeWritten
||
1694 (Record
->TimeWritten
>= RecBuf
.TimeWritten
&&
1695 Record
->TimeWritten
- RecBuf
.TimeWritten
< LogFile
->Header
.Retention
)))
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;
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
1709 OldestRecordNumber
++;
1710 RecOffset
+= RecBuf
.Length
;
1711 FreeSpace
+= RecBuf
.Length
;
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.
1718 if (LogFile
->Header
.MaxSize
- RecOffset
< sizeof(EVENTLOGRECORD
))
1720 /* Add the padding size */
1721 FreeSpace
+= LogFile
->Header
.MaxSize
- RecOffset
;
1725 DPRINT("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace
, BufSize
);
1727 /* The log records are wrapping */
1728 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1731 // FIXME: May lead to corruption if the other subsequent calls fail...
1734 * We have validated all the region of events to be discarded,
1735 * now we can perform their deletion.
1737 LogfDeleteOffsetInformation(LogFile
, OrgOldestRecordNumber
, OldestRecordNumber
- 1);
1738 LogFile
->Header
.OldestRecordNumber
= OldestRecordNumber
;
1739 LogFile
->Header
.StartOffset
= LogfOffsetByNumber(LogFile
, OldestRecordNumber
);
1740 if (LogFile
->Header
.StartOffset
== 0)
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.
1746 LogFile
->Header
.StartOffset
= sizeof(EVENTLOGHEADER
);
1747 WriteOffset
= LogFile
->Header
.StartOffset
;
1748 LogFile
->Header
.EndOffset
= WriteOffset
;
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
);
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'...
1763 if (LogFile
->CurrentSize
< LogFile
->Header
.MaxSize
)
1765 DPRINT1("Expanding the log file from %lu to %lu\n",
1766 LogFile
->CurrentSize
, LogFile
->Header
.MaxSize
);
1768 /* For the moment this is a trivial operation */
1769 LogFile
->CurrentSize
= LogFile
->Header
.MaxSize
;
1772 /* Pad the end of the log */
1773 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1774 if (WriteOffset
< LogFile
->Header
.EndOffset
)
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);
1780 FileOffset
.QuadPart
= LogFile
->Header
.EndOffset
;
1781 Status
= NtWriteFile(LogFile
->hFile
,
1790 // dwWritten = IoStatusBlock.Information;
1791 if (!NT_SUCCESS(Status
))
1793 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1798 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1799 FileOffset
.QuadPart
= WriteOffset
;
1800 Status
= WriteLogBuffer(LogFile
,
1806 // dwWritten = IoStatusBlock.Information;
1807 if (!NT_SUCCESS(Status
))
1809 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1812 /* FileOffset now contains the offset just after the end of the record buffer */
1813 FileOffset
= NextOffset
;
1815 if (!LogfAddOffsetInformation(LogFile
,
1816 Record
->RecordNumber
,
1819 Status
= STATUS_NO_MEMORY
; // STATUS_EVENTLOG_FILE_CORRUPT;
1823 LogFile
->Header
.CurrentRecordNumber
++;
1824 if (LogFile
->Header
.CurrentRecordNumber
== 0)
1825 LogFile
->Header
.CurrentRecordNumber
= 1;
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.
1832 LogFile
->Header
.EndOffset
= FileOffset
.QuadPart
;
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
;
1840 // FileOffset.QuadPart = LogFile->Header.EndOffset;
1841 Status
= WriteLogBuffer(LogFile
,
1847 // dwWritten = IoStatusBlock.Information;
1848 if (!NT_SUCCESS(Status
))
1850 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1853 FileOffset
= NextOffset
;
1855 LogFile
->Header
.Flags
&= ELF_LOGFILE_HEADER_DIRTY
;
1857 /* Update the event log header */
1858 FileOffset
.QuadPart
= 0LL;
1859 Status
= NtWriteFile(LogFile
->hFile
,
1865 sizeof(EVENTLOGHEADER
),
1868 // dwWritten = IoStatusBlock.Information;
1869 if (!NT_SUCCESS(Status
))
1871 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1875 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1876 if (!NT_SUCCESS(Status
))
1878 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
1882 Status
= STATUS_SUCCESS
;
1885 RtlReleaseResource(&LogFile
->Lock
);
1890 LogfClearFile(PLOGFILE LogFile
,
1891 PUNICODE_STRING BackupFileName
)
1895 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1897 if (BackupFileName
->Length
> 0)
1899 /* Write a backup file */
1900 Status
= LogfBackupFile(LogFile
, BackupFileName
);
1901 if (!NT_SUCCESS(Status
))
1903 DPRINT1("LogfBackupFile failed (Status: 0x%08lx)\n", Status
);
1908 Status
= LogfInitializeNew(LogFile
,
1909 LogFile
->Header
.MaxSize
,
1910 LogFile
->Header
.Retention
);
1911 if (!NT_SUCCESS(Status
))
1913 DPRINT1("LogfInitializeNew failed (Status: 0x%08lx)\n", Status
);
1917 RtlReleaseResource(&LogFile
->Lock
);
1922 LogfBackupFile(PLOGFILE LogFile
,
1923 PUNICODE_STRING BackupFileName
)
1926 OBJECT_ATTRIBUTES ObjectAttributes
;
1927 IO_STATUS_BLOCK IoStatusBlock
;
1928 LARGE_INTEGER FileOffset
;
1929 HANDLE FileHandle
= NULL
;
1930 EVENTLOGHEADER Header
;
1931 EVENTLOGRECORD RecBuf
;
1935 PVOID Buffer
= NULL
;
1939 DPRINT1("LogfBackupFile(%p, %wZ)\n", LogFile
, BackupFileName
);
1941 /* Lock the log file shared */
1942 RtlAcquireResourceShared(&LogFile
->Lock
, TRUE
);
1944 InitializeObjectAttributes(&ObjectAttributes
,
1946 OBJ_CASE_INSENSITIVE
,
1950 Status
= NtCreateFile(&FileHandle
,
1951 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
1955 FILE_ATTRIBUTE_NORMAL
,
1958 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
1961 if (!NT_SUCCESS(Status
))
1963 DPRINT("Cannot create backup file %wZ (Status: 0x%08lx)\n", BackupFileName
, Status
);
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
);
1981 /* Write the (dirty) log file header */
1982 FileOffset
.QuadPart
= 0LL;
1983 Status
= NtWriteFile(FileHandle
,
1992 if (!NT_SUCCESS(Status
))
1994 DPRINT1("Failed to write the log file header (Status: 0x%08lx)\n", Status
);
1998 for (i
= LogFile
->Header
.OldestRecordNumber
; i
< LogFile
->Header
.CurrentRecordNumber
; i
++)
2000 RecOffset
= LogfOffsetByNumber(LogFile
, i
);
2004 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
2005 FileOffset
.QuadPart
= RecOffset
;
2006 Status
= NtReadFile(LogFile
->hFile
,
2015 if (!NT_SUCCESS(Status
))
2017 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
2020 // dwRead = IoStatusBlock.Information;
2022 // if (dwRead != sizeof(RecBuf))
2025 Buffer
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
2028 DPRINT1("RtlAllocateHeap() failed!\n");
2032 /* Read the full EVENTLOGRECORD (header + data) with wrapping */
2033 Status
= ReadLogBuffer(LogFile
,
2039 if (!NT_SUCCESS(Status
))
2041 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
2042 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
2043 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
2046 // dwRead = IoStatusBlock.Information;
2048 /* Write the event record (no wrap for the backup log) */
2049 Status
= NtWriteFile(FileHandle
,
2058 if (!NT_SUCCESS(Status
))
2060 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status
);
2061 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
2065 /* Update the header information */
2066 Header
.EndOffset
+= RecBuf
.Length
;
2068 /* Free the buffer */
2069 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
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
;
2080 /* Write the EOF record (no wrap for the backup log) */
2081 Status
= NtWriteFile(FileHandle
,
2090 if (!NT_SUCCESS(Status
))
2092 DPRINT1("NtWriteFile() failed!\n");
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
));
2102 /* Write the (clean) log file header */
2103 FileOffset
.QuadPart
= 0LL;
2104 Status
= NtWriteFile(FileHandle
,
2113 if (!NT_SUCCESS(Status
))
2115 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status
);
2119 /* Close the backup file */
2120 if (FileHandle
!= NULL
)
2121 NtClose(FileHandle
);
2123 /* Unlock the log file */
2124 RtlReleaseResource(&LogFile
->Lock
);
2131 LogfAllocAndBuildNewRecord(PULONG lpRecSize
,
2137 PCWSTR ComputerName
,
2146 PEVENTLOGRECORD pRec
;
2149 SIZE_T SourceNameLen
, ComputerNameLen
, StringLen
;
2152 SourceNameLen
= (SourceName
? wcslen(SourceName
) : 0) + 1;
2153 ComputerNameLen
= (ComputerName
? wcslen(ComputerName
) : 0) + 1;
2155 dwRecSize
= sizeof(EVENTLOGRECORD
) + (SourceNameLen
+ ComputerNameLen
) * sizeof(WCHAR
);
2157 /* Align on DWORD boundary for the SID */
2158 dwRecSize
= ROUND_UP(dwRecSize
, sizeof(ULONG
));
2160 dwRecSize
+= dwSidLength
;
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
++)
2167 StringLen
= wcslen(str
) + 1; // str must be != NULL
2168 dwRecSize
+= StringLen
* sizeof(WCHAR
);
2172 /* Add the data size */
2173 dwRecSize
+= dwDataSize
;
2175 /* Align on DWORD boundary for the full structure */
2176 dwRecSize
= ROUND_UP(dwRecSize
, sizeof(ULONG
));
2178 /* Size of the trailing 'Length' member */
2179 dwRecSize
+= sizeof(ULONG
);
2181 Buffer
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwRecSize
);
2184 DPRINT1("Cannot allocate heap!\n");
2188 pRec
= (PEVENTLOGRECORD
)Buffer
;
2189 pRec
->Length
= dwRecSize
;
2190 pRec
->Reserved
= LOGFILE_SIGNATURE
;
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.
2198 pRec
->RecordNumber
= 0;
2201 * Set the generated time, and temporarily set the written time
2202 * with the generated time.
2204 pRec
->TimeGenerated
= Time
;
2205 pRec
->TimeWritten
= Time
;
2207 pRec
->EventID
= dwEventId
;
2208 pRec
->EventType
= wType
;
2209 pRec
->EventCategory
= wCategory
;
2211 pos
= sizeof(EVENTLOGRECORD
);
2214 StringCchCopy((PWSTR
)(Buffer
+ pos
), SourceNameLen
, SourceName
);
2215 pos
+= SourceNameLen
* sizeof(WCHAR
);
2217 StringCchCopy((PWSTR
)(Buffer
+ pos
), ComputerNameLen
, ComputerName
);
2218 pos
+= ComputerNameLen
* sizeof(WCHAR
);
2220 /* Align on DWORD boundary for the SID */
2221 pos
= ROUND_UP(pos
, sizeof(ULONG
));
2223 pRec
->UserSidLength
= 0;
2224 pRec
->UserSidOffset
= 0;
2227 RtlCopyMemory(Buffer
+ pos
, lpUserSid
, dwSidLength
);
2228 pRec
->UserSidLength
= dwSidLength
;
2229 pRec
->UserSidOffset
= pos
;
2233 pRec
->StringOffset
= pos
;
2234 for (i
= 0, str
= lpStrings
; i
< wNumStrings
; i
++)
2236 StringLen
= wcslen(str
) + 1; // str must be != NULL
2237 StringCchCopy((PWSTR
)(Buffer
+ pos
), StringLen
, str
);
2239 pos
+= StringLen
* sizeof(WCHAR
);
2241 pRec
->NumStrings
= wNumStrings
;
2243 pRec
->DataLength
= 0;
2244 pRec
->DataOffset
= 0;
2247 RtlCopyMemory(Buffer
+ pos
, lpRawData
, dwDataSize
);
2248 pRec
->DataLength
= dwDataSize
;
2249 pRec
->DataOffset
= pos
;
2253 /* Align on DWORD boundary for the full structure */
2254 pos
= ROUND_UP(pos
, sizeof(ULONG
));
2256 /* Initialize the trailing 'Length' member */
2257 *((PDWORD
) (Buffer
+ pos
)) = dwRecSize
;
2259 *lpRecSize
= dwRecSize
;
2264 LogfReportEvent(USHORT wType
,
2273 WCHAR szComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
2274 DWORD dwComputerNameLength
= MAX_COMPUTERNAME_LENGTH
+ 1;
2275 PEVENTLOGRECORD logBuffer
;
2276 LARGE_INTEGER SystemTime
;
2280 if (!EventLogSource
)
2283 if (!GetComputerNameW(szComputerName
, &dwComputerNameLength
))
2285 szComputerName
[0] = L
'\0';
2288 NtQuerySystemTime(&SystemTime
);
2289 RtlTimeToSecondsSince1970(&SystemTime
, &Time
);
2291 logBuffer
= LogfAllocAndBuildNewRecord(&recSize
,
2296 EventLogSource
->szName
,
2305 Status
= LogfWriteRecord(EventLogSource
->LogFile
, recSize
, logBuffer
);
2306 if (!NT_SUCCESS(Status
))
2308 DPRINT1("ERROR WRITING TO EventLog %S (Status 0x%08lx)\n",
2309 EventLogSource
->LogFile
->FileName
, Status
);
2312 LogfFreeRecord(logBuffer
);