2 * PROJECT: ReactOS EventLog Service
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/services/eventlog/file.c
5 * PURPOSE: Event log file support wrappers
6 * COPYRIGHT: Copyright 2005 Saveliy Tretiakov
11 /* INCLUDES ******************************************************************/
14 #include <ndk/iofuncs.h>
15 #include <ndk/kefuncs.h>
20 /* LOG FILE LIST - GLOBALS ***************************************************/
22 static LIST_ENTRY LogFileListHead
;
23 static CRITICAL_SECTION LogFileListCs
;
25 /* LOG FILE LIST - FUNCTIONS *************************************************/
27 VOID
LogfCloseAll(VOID
)
29 EnterCriticalSection(&LogFileListCs
);
31 while (!IsListEmpty(&LogFileListHead
))
33 LogfClose(CONTAINING_RECORD(LogFileListHead
.Flink
, LOGFILE
, ListEntry
), TRUE
);
36 LeaveCriticalSection(&LogFileListCs
);
38 DeleteCriticalSection(&LogFileListCs
);
41 VOID
LogfListInitialize(VOID
)
43 InitializeCriticalSection(&LogFileListCs
);
44 InitializeListHead(&LogFileListHead
);
47 PLOGFILE
LogfListItemByName(LPCWSTR Name
)
49 PLIST_ENTRY CurrentEntry
;
50 PLOGFILE Item
, Result
= NULL
;
54 EnterCriticalSection(&LogFileListCs
);
56 CurrentEntry
= LogFileListHead
.Flink
;
57 while (CurrentEntry
!= &LogFileListHead
)
59 Item
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
61 if (Item
->LogName
&& !_wcsicmp(Item
->LogName
, Name
))
67 CurrentEntry
= CurrentEntry
->Flink
;
70 LeaveCriticalSection(&LogFileListCs
);
75 /* Index starting from 1 */
76 DWORD
LogfListItemIndexByName(LPCWSTR Name
)
78 PLIST_ENTRY CurrentEntry
;
84 EnterCriticalSection(&LogFileListCs
);
86 CurrentEntry
= LogFileListHead
.Flink
;
87 while (CurrentEntry
!= &LogFileListHead
)
89 PLOGFILE Item
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
91 if (Item
->LogName
&& !_wcsicmp(Item
->LogName
, Name
))
97 CurrentEntry
= CurrentEntry
->Flink
;
101 LeaveCriticalSection(&LogFileListCs
);
106 /* Index starting from 1 */
107 PLOGFILE
LogfListItemByIndex(DWORD Index
)
109 PLIST_ENTRY CurrentEntry
;
110 PLOGFILE Result
= NULL
;
113 EnterCriticalSection(&LogFileListCs
);
115 CurrentEntry
= LogFileListHead
.Flink
;
116 while (CurrentEntry
!= &LogFileListHead
)
120 Result
= CONTAINING_RECORD(CurrentEntry
, LOGFILE
, ListEntry
);
124 CurrentEntry
= CurrentEntry
->Flink
;
128 LeaveCriticalSection(&LogFileListCs
);
132 DWORD
LogfListItemCount(VOID
)
134 PLIST_ENTRY CurrentEntry
;
137 EnterCriticalSection(&LogFileListCs
);
139 CurrentEntry
= LogFileListHead
.Flink
;
140 while (CurrentEntry
!= &LogFileListHead
)
142 CurrentEntry
= CurrentEntry
->Flink
;
146 LeaveCriticalSection(&LogFileListCs
);
151 LogfListAddItem(PLOGFILE Item
)
153 EnterCriticalSection(&LogFileListCs
);
154 InsertTailList(&LogFileListHead
, &Item
->ListEntry
);
155 LeaveCriticalSection(&LogFileListCs
);
159 LogfListRemoveItem(PLOGFILE Item
)
161 EnterCriticalSection(&LogFileListCs
);
162 RemoveEntryList(&Item
->ListEntry
);
163 LeaveCriticalSection(&LogFileListCs
);
167 /* GLOBALS *******************************************************************/
169 static const EVENTLOGEOF EOFRecord
=
172 0x11111111, 0x22222222, 0x33333333, 0x44444444,
177 /* FUNCTIONS *****************************************************************/
180 ReadLogBuffer(IN PLOGFILE LogFile
,
181 OUT PIO_STATUS_BLOCK IoStatusBlock
,
184 IN PLARGE_INTEGER ByteOffset
,
185 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
189 LARGE_INTEGER FileOffset
;
191 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
192 // ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize);
193 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
196 NextOffset
->QuadPart
= 0LL;
198 /* Read the first part of the buffer */
199 FileOffset
= *ByteOffset
;
200 BufSize
= min(Length
, LogFile
->CurrentSize
- FileOffset
.QuadPart
);
202 Status
= NtReadFile(LogFile
->hFile
,
211 if (!NT_SUCCESS(Status
))
213 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
217 if (Length
> BufSize
)
219 ULONG_PTR Information
= IoStatusBlock
->Information
;
222 * The buffer was splitted in two, its second part
223 * is to be read at the beginning of the log.
225 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
226 BufSize
= Length
- BufSize
;
227 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
229 Status
= NtReadFile(LogFile
->hFile
,
238 if (!NT_SUCCESS(Status
))
240 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
243 /* Add the read number of bytes from the first read */
244 IoStatusBlock
->Information
+= Information
;
247 /* We return the offset just after the end of the read buffer */
249 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
255 WriteLogBuffer(IN PLOGFILE LogFile
,
256 OUT PIO_STATUS_BLOCK IoStatusBlock
,
259 IN PLARGE_INTEGER ByteOffset
,
260 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
264 LARGE_INTEGER FileOffset
;
266 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
267 ASSERT(ByteOffset
->QuadPart
<= LogFile
->Header
.MaxSize
);
268 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
271 NextOffset
->QuadPart
= 0LL;
273 /* Write the first part of the buffer */
274 FileOffset
= *ByteOffset
;
275 BufSize
= min(Length
, LogFile
->CurrentSize
/* LogFile->Header.MaxSize */ - FileOffset
.QuadPart
);
277 Status
= NtWriteFile(LogFile
->hFile
,
286 if (!NT_SUCCESS(Status
))
288 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
292 if (Length
> BufSize
)
294 ULONG_PTR Information
= IoStatusBlock
->Information
;
297 * The buffer was splitted in two, its second part is written
298 * at the beginning of the log.
300 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
301 BufSize
= Length
- BufSize
;
302 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
304 Status
= NtWriteFile(LogFile
->hFile
,
313 if (!NT_SUCCESS(Status
))
315 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
318 /* Add the written number of bytes from the first write */
319 IoStatusBlock
->Information
+= Information
;
322 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
325 /* We return the offset just after the end of the written buffer */
327 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
333 /* Returns 0 if nothing is found */
335 LogfOffsetByNumber(PLOGFILE LogFile
,
340 for (i
= 0; i
< LogFile
->OffsetInfoNext
; i
++)
342 if (LogFile
->OffsetInfo
[i
].EventNumber
== RecordNumber
)
343 return LogFile
->OffsetInfo
[i
].EventOffset
;
349 LogfAddOffsetInformation(PLOGFILE LogFile
,
355 if (LogFile
->OffsetInfoNext
== LogFile
->OffsetInfoSize
)
357 NewOffsetInfo
= HeapReAlloc(GetProcessHeap(),
360 (LogFile
->OffsetInfoSize
+ 64) *
361 sizeof(EVENT_OFFSET_INFO
));
365 DPRINT1("Cannot reallocate heap.\n");
369 LogFile
->OffsetInfo
= (PEVENT_OFFSET_INFO
)NewOffsetInfo
;
370 LogFile
->OffsetInfoSize
+= 64;
373 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventNumber
= ulNumber
;
374 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventOffset
= ulOffset
;
375 LogFile
->OffsetInfoNext
++;
381 LogfDeleteOffsetInformation(PLOGFILE LogFile
,
387 if (ulNumberMin
> ulNumberMax
)
390 /* Remove records ulNumberMin to ulNumberMax inclusive */
391 while (ulNumberMin
<= ulNumberMax
)
394 * As the offset information is listed in increasing order, and we want
395 * to keep the list without holes, we demand that ulNumberMin is the first
396 * element in the list.
398 if (ulNumberMin
!= LogFile
->OffsetInfo
[0].EventNumber
)
402 * RtlMoveMemory(&LogFile->OffsetInfo[0],
403 * &LogFile->OffsetInfo[1],
404 * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
406 for (i
= 0; i
< LogFile
->OffsetInfoNext
- 1; i
++)
408 LogFile
->OffsetInfo
[i
].EventNumber
= LogFile
->OffsetInfo
[i
+ 1].EventNumber
;
409 LogFile
->OffsetInfo
[i
].EventOffset
= LogFile
->OffsetInfo
[i
+ 1].EventOffset
;
411 LogFile
->OffsetInfoNext
--;
413 /* Go to the next offset information */
421 LogfInitializeNew(PLOGFILE LogFile
,
426 IO_STATUS_BLOCK IoStatusBlock
;
427 LARGE_INTEGER FileOffset
;
430 /* Initialize the event log header */
431 RtlZeroMemory(&LogFile
->Header
, sizeof(EVENTLOGHEADER
));
433 LogFile
->Header
.HeaderSize
= sizeof(EVENTLOGHEADER
);
434 LogFile
->Header
.Signature
= LOGFILE_SIGNATURE
;
435 LogFile
->Header
.MajorVersion
= MAJORVER
;
436 LogFile
->Header
.MinorVersion
= MINORVER
;
438 /* Set the offset to the oldest record */
439 LogFile
->Header
.StartOffset
= sizeof(EVENTLOGHEADER
);
440 /* Set the offset to the ELF_EOF_RECORD */
441 LogFile
->Header
.EndOffset
= sizeof(EVENTLOGHEADER
);
442 /* Set the number of the next record that will be added */
443 LogFile
->Header
.CurrentRecordNumber
= 1;
444 /* The event log is empty, there is no record so far */
445 LogFile
->Header
.OldestRecordNumber
= 0;
447 // FIXME: Windows' EventLog log file sizes are always multiple of 64kB
448 // but that does not mean the real log size is == file size.
450 /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */
451 LogFile
->Header
.MaxSize
= ROUND_UP(ulMaxSize
, sizeof(ULONG
));
452 LogFile
->CurrentSize
= LogFile
->Header
.MaxSize
;
454 LogFile
->Header
.Flags
= 0;
455 LogFile
->Header
.Retention
= ulRetention
;
456 LogFile
->Header
.EndHeaderSize
= sizeof(EVENTLOGHEADER
);
458 /* Write the header */
459 SetFilePointer(LogFile
->hFile
, 0, NULL
, FILE_BEGIN
);
460 SetEndOfFile(LogFile
->hFile
);
462 FileOffset
.QuadPart
= 0LL;
463 Status
= NtWriteFile(LogFile
->hFile
,
469 sizeof(EVENTLOGHEADER
),
472 if (!NT_SUCCESS(Status
))
474 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
478 /* Initialize the ELF_EOF_RECORD and write it */
479 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
480 EofRec
.BeginRecord
= LogFile
->Header
.StartOffset
;
481 EofRec
.EndRecord
= LogFile
->Header
.EndOffset
;
482 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
483 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
485 Status
= NtWriteFile(LogFile
->hFile
,
494 if (!NT_SUCCESS(Status
))
496 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
500 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
501 if (!NT_SUCCESS(Status
))
503 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
507 return STATUS_SUCCESS
;
511 LogfInitializeExisting(PLOGFILE LogFile
,
515 IO_STATUS_BLOCK IoStatusBlock
;
516 LARGE_INTEGER FileOffset
, NextOffset
;
517 LARGE_INTEGER LogFileSize
;
518 DWORD dwRecordsNumber
= 0;
522 EVENTLOGRECORD RecBuf
;
523 PEVENTLOGRECORD pRecBuf
;
524 BOOLEAN Wrapping
= FALSE
;
525 BOOLEAN IsLogDirty
= FALSE
;
527 DPRINT("Initializing LogFile %S\n", LogFile
->LogName
);
529 /* Read the log header */
530 FileOffset
.QuadPart
= 0LL;
531 Status
= NtReadFile(LogFile
->hFile
,
537 sizeof(EVENTLOGHEADER
),
540 if (!NT_SUCCESS(Status
))
542 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
543 return STATUS_EVENTLOG_FILE_CORRUPT
;
545 if (IoStatusBlock
.Information
!= sizeof(EVENTLOGHEADER
))
547 DPRINT("EventLog: Invalid file %S.\n", LogFile
->FileName
);
548 return STATUS_EVENTLOG_FILE_CORRUPT
;
551 /* Header validity checks */
553 if (LogFile
->Header
.HeaderSize
!= sizeof(EVENTLOGHEADER
) ||
554 LogFile
->Header
.EndHeaderSize
!= sizeof(EVENTLOGHEADER
))
556 DPRINT("EventLog: Invalid header size in %S.\n", LogFile
->FileName
);
557 return STATUS_EVENTLOG_FILE_CORRUPT
;
560 if (LogFile
->Header
.Signature
!= LOGFILE_SIGNATURE
)
562 DPRINT("EventLog: Invalid signature %x in %S.\n",
563 LogFile
->Header
.Signature
, LogFile
->FileName
);
564 return STATUS_EVENTLOG_FILE_CORRUPT
;
567 IsLogDirty
= (LogFile
->Header
.Flags
& ELF_LOGFILE_HEADER_DIRTY
);
569 /* If the log is a backup log that is dirty, then it is corrupted */
570 if (Backup
&& IsLogDirty
)
572 DPRINT("EventLog: Backup log %S is dirty.\n", LogFile
->FileName
);
573 return STATUS_EVENTLOG_FILE_CORRUPT
;
577 * Retrieve the log file size and check whether the file is not too large;
578 * this log format only supports files of theoretical size < 0xFFFFFFFF .
580 if (!GetFileSizeEx(LogFile
->hFile
, &LogFileSize
))
581 return I_RpcMapWin32Status(GetLastError());
583 if (LogFileSize
.HighPart
!= 0)
585 DPRINT1("EventLog: Log %S is too large.\n", LogFile
->FileName
);
586 // return STATUS_FILE_TOO_LARGE;
587 return STATUS_EVENTLOG_FILE_CORRUPT
;
590 LogFile
->CurrentSize
= LogFileSize
.LowPart
; // LogFileSize.QuadPart;
592 /* Adjust the log maximum size if needed */
593 if (LogFile
->CurrentSize
> LogFile
->Header
.MaxSize
)
594 LogFile
->Header
.MaxSize
= LogFile
->CurrentSize
;
597 * In a non-backup dirty log, the most up-to-date information about
598 * the Start/End offsets and the Oldest and Current event record numbers
599 * are found in the EOF record. We need to locate the EOF record without
600 * relying on the log header's EndOffset, then patch the log header with
601 * the values from the EOF record.
603 if ((LogFile
->Header
.EndOffset
>= sizeof(EVENTLOGHEADER
)) &&
604 (LogFile
->Header
.EndOffset
< LogFile
->CurrentSize
) &&
605 (LogFile
->Header
.EndOffset
& 3) == 0) // EndOffset % sizeof(ULONG) == 0
607 /* The header EOF offset may be valid, try to start with it */
608 RecOffset
= LogFile
->Header
.EndOffset
;
612 /* The header EOF offset could not be valid, so start from the beginning */
613 RecOffset
= sizeof(EVENTLOGHEADER
);
616 FileOffset
.QuadPart
= RecOffset
;
621 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
623 DPRINT1("EOF record not found!\n");
624 return STATUS_EVENTLOG_FILE_CORRUPT
;
627 /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
628 Status
= ReadLogBuffer(LogFile
,
631 EVENTLOGEOF_SIZE_FIXED
,
634 if (!NT_SUCCESS(Status
))
636 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
637 return STATUS_EVENTLOG_FILE_CORRUPT
;
639 if (IoStatusBlock
.Information
!= EVENTLOGEOF_SIZE_FIXED
)
641 DPRINT1("Cannot read at most an EOF record!\n");
642 return STATUS_EVENTLOG_FILE_CORRUPT
;
645 /* Is it an EVENTLOGEOF record? */
646 if (RtlCompareMemory(&EofRec
, &EOFRecord
, EVENTLOGEOF_SIZE_FIXED
) == EVENTLOGEOF_SIZE_FIXED
)
648 DPRINT("Found EOF record at %llx\n", FileOffset
.QuadPart
);
650 /* Got it! Break the loop and continue */
654 /* No, continue looping */
655 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
656 FileOffset
.QuadPart
+= sizeof(ULONG
);
658 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 2*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
659 FileOffset
.QuadPart
+= 2*sizeof(ULONG
);
661 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 3*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
662 FileOffset
.QuadPart
+= 3*sizeof(ULONG
);
664 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 4*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
665 FileOffset
.QuadPart
+= 4*sizeof(ULONG
);
667 FileOffset
.QuadPart
+= 5*sizeof(ULONG
); // EVENTLOGEOF_SIZE_FIXED
669 if (FileOffset
.QuadPart
>= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */)
671 /* Wrap the offset */
672 FileOffset
.QuadPart
-= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER
);
677 * The only way to be there is to have found a valid EOF record.
678 * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT
682 /* Read the full EVENTLOGEOF (may wrap) and validate it */
683 Status
= ReadLogBuffer(LogFile
,
689 if (!NT_SUCCESS(Status
))
691 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
692 return STATUS_EVENTLOG_FILE_CORRUPT
;
694 if (IoStatusBlock
.Information
!= sizeof(EofRec
))
696 DPRINT1("Cannot read the full EOF record!\n");
697 return STATUS_EVENTLOG_FILE_CORRUPT
;
700 /* Complete validity checks */
701 if ((EofRec
.RecordSizeEnd
!= EofRec
.RecordSizeBeginning
) ||
702 (EofRec
.EndRecord
!= FileOffset
.QuadPart
))
704 DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected %x %x!\n", FileOffset
.QuadPart
,
705 EofRec
.RecordSizeEnd
, EofRec
.RecordSizeBeginning
,
706 EofRec
.EndRecord
, FileOffset
.QuadPart
,
707 EOFRecord
.RecordSizeEnd
, EOFRecord
.RecordSizeBeginning
);
708 DPRINT1("RecordSizeEnd = %x\n", EofRec
.RecordSizeEnd
);
709 DPRINT1("RecordSizeBeginning = %x\n", EofRec
.RecordSizeBeginning
);
710 DPRINT1("EndRecord = %x\n", EofRec
.EndRecord
);
711 return STATUS_EVENTLOG_FILE_CORRUPT
;
714 /* The EOF record is valid, break the loop and continue */
716 /* If the log is not dirty, the header values should correspond to the EOF ones */
719 if ( (LogFile
->Header
.StartOffset
!= EofRec
.BeginRecord
) ||
720 (LogFile
->Header
.EndOffset
!= EofRec
.EndRecord
) ||
721 (LogFile
->Header
.CurrentRecordNumber
!= EofRec
.CurrentRecordNumber
) ||
722 (LogFile
->Header
.OldestRecordNumber
!= EofRec
.OldestRecordNumber
) )
725 "Log header or EOF record is corrupted:\n"
726 " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n"
727 " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n",
728 LogFile
->Header
.StartOffset
, EofRec
.BeginRecord
,
729 LogFile
->Header
.EndOffset
, EofRec
.EndRecord
,
730 LogFile
->Header
.CurrentRecordNumber
, EofRec
.CurrentRecordNumber
,
731 LogFile
->Header
.OldestRecordNumber
, EofRec
.OldestRecordNumber
);
733 return STATUS_EVENTLOG_FILE_CORRUPT
;
737 /* If the log is dirty, patch the log header with the values from the EOF record */
738 if (!Backup
&& IsLogDirty
)
740 LogFile
->Header
.StartOffset
= EofRec
.BeginRecord
;
741 LogFile
->Header
.EndOffset
= EofRec
.EndRecord
;
742 LogFile
->Header
.CurrentRecordNumber
= EofRec
.CurrentRecordNumber
;
743 LogFile
->Header
.OldestRecordNumber
= EofRec
.OldestRecordNumber
;
747 * FIXME! During operations the EOF record is the one that is the most
748 * updated (its Oldest & Current record numbers are always up-to
749 * date) while the ones from the header may be unsync. When closing
750 * (or flushing?) the event log, the header's record numbers get
751 * updated with the same values as the ones stored in the EOF record.
754 /* Verify Start/End offsets boundaries */
756 if ((LogFile
->Header
.StartOffset
>= LogFile
->CurrentSize
) ||
757 (LogFile
->Header
.StartOffset
& 3) != 0) // StartOffset % sizeof(ULONG) != 0
759 DPRINT("EventLog: Invalid start offset %x in %S.\n",
760 LogFile
->Header
.StartOffset
, LogFile
->FileName
);
761 return STATUS_EVENTLOG_FILE_CORRUPT
;
763 if ((LogFile
->Header
.EndOffset
>= LogFile
->CurrentSize
) ||
764 (LogFile
->Header
.EndOffset
& 3) != 0) // EndOffset % sizeof(ULONG) != 0
766 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
767 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
768 return STATUS_EVENTLOG_FILE_CORRUPT
;
771 if ((LogFile
->Header
.StartOffset
!= LogFile
->Header
.EndOffset
) &&
772 (LogFile
->Header
.MaxSize
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
775 * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
776 * it should point to a non-splitted EVENTLOGRECORD.
778 DPRINT("EventLog: Invalid start offset %x in %S.\n",
779 LogFile
->Header
.StartOffset
, LogFile
->FileName
);
780 return STATUS_EVENTLOG_FILE_CORRUPT
;
783 if ((LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
784 (LogFile
->Header
.EndOffset
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
787 * In non-wrapping case, there must be enough space between StartOffset
788 * and EndOffset to contain at least a full EVENTLOGRECORD.
790 DPRINT("EventLog: Invalid start offset %x or end offset %x in %S.\n",
791 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
, LogFile
->FileName
);
792 return STATUS_EVENTLOG_FILE_CORRUPT
;
795 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
798 * Non-wrapping case: the (wrapping) free space starting at EndOffset
799 * must be able to contain an EVENTLOGEOF.
801 if (LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
+
802 LogFile
->Header
.StartOffset
- sizeof(EVENTLOGHEADER
) < sizeof(EVENTLOGEOF
))
804 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
805 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
806 return STATUS_EVENTLOG_FILE_CORRUPT
;
809 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
812 * Wrapping case: the free space between EndOffset and StartOffset
813 * must be able to contain an EVENTLOGEOF.
815 if (LogFile
->Header
.StartOffset
- LogFile
->Header
.EndOffset
< sizeof(EVENTLOGEOF
))
817 DPRINT("EventLog: Invalid EOF offset %x in %S.\n",
818 LogFile
->Header
.EndOffset
, LogFile
->FileName
);
819 return STATUS_EVENTLOG_FILE_CORRUPT
;
823 /* Start enumerating the event records from the beginning */
824 RecOffset
= LogFile
->Header
.StartOffset
;
825 FileOffset
.QuadPart
= RecOffset
;
829 // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
831 // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
832 // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
835 DPRINT("StartOffset = %x, EndOffset = %x\n",
836 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
839 * For non-backup logs of size < MaxSize, reorganize the events such that
840 * they do not wrap as soon as we write new ones.
845 pRecBuf
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
848 DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
857 DPRINT("StartOffset = %x, EndOffset = %x\n",
858 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
861 while (FileOffset
.QuadPart
!= LogFile
->Header
.EndOffset
)
863 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
865 /* We have finished enumerating all the event records */
869 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
870 Status
= NtReadFile(LogFile
->hFile
,
879 if (!NT_SUCCESS(Status
))
881 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
882 return STATUS_EVENTLOG_FILE_CORRUPT
;
884 if (IoStatusBlock
.Information
!= sizeof(RecBuf
))
886 DPRINT("Length != sizeof(RecBuf)\n");
890 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
||
891 RecBuf
.Length
< sizeof(EVENTLOGRECORD
))
893 DPRINT("RecBuf problem\n");
897 /* Allocate a full EVENTLOGRECORD (header + data) */
898 pRecBuf
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
901 DPRINT1("Cannot allocate heap!\n");
902 return STATUS_NO_MEMORY
;
905 /* Attempt to read the full EVENTLOGRECORD (can wrap) */
906 Status
= ReadLogBuffer(LogFile
,
912 if (!NT_SUCCESS(Status
))
914 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
915 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
916 return STATUS_EVENTLOG_FILE_CORRUPT
;
918 if (IoStatusBlock
.Information
!= RecBuf
.Length
)
920 DPRINT1("Oh oh!!\n");
921 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
925 // /* If OverWrittenRecords is TRUE and this record has already been read */
926 // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
928 // RtlFreeHeap(GetProcessHeap(), 0, pRecBuf);
932 pdwRecSize2
= (PDWORD
)((ULONG_PTR
)pRecBuf
+ RecBuf
.Length
- 4);
934 if (*pdwRecSize2
!= RecBuf
.Length
)
936 DPRINT1("Invalid RecordSizeEnd of record %d (%x) in %S\n",
937 dwRecordsNumber
, *pdwRecSize2
, LogFile
->LogName
);
938 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
942 DPRINT("Add new record %d - %x\n", pRecBuf
->RecordNumber
, FileOffset
.QuadPart
);
946 if (!LogfAddOffsetInformation(LogFile
,
947 pRecBuf
->RecordNumber
,
948 FileOffset
.QuadPart
))
950 DPRINT1("LogfAddOffsetInformation() failed!\n");
951 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
952 return STATUS_EVENTLOG_FILE_CORRUPT
;
955 RtlFreeHeap(GetProcessHeap(), 0, pRecBuf
);
957 if (NextOffset
.QuadPart
== LogFile
->Header
.EndOffset
)
959 /* We have finished enumerating all the event records */
960 DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
965 * If this was the last event record before the end of the log file,
966 * the next one should start at the beginning of the log and the space
967 * between the last event record and the end of the file is padded.
969 if (LogFile
->Header
.MaxSize
- NextOffset
.QuadPart
< sizeof(EVENTLOGRECORD
))
971 /* Wrap to the beginning of the log */
973 NextOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
977 * If the next offset to read is below the current offset,
978 * this means we are wrapping.
980 if (FileOffset
.QuadPart
> NextOffset
.QuadPart
)
982 DPRINT("Wrapping = TRUE;\n");
986 /* Move the current offset */
987 FileOffset
= NextOffset
;
990 /* If the event log was empty, it will now contain one record */
991 if (dwRecordsNumber
!= 0 && LogFile
->Header
.OldestRecordNumber
== 0)
992 LogFile
->Header
.OldestRecordNumber
= 1;
994 LogFile
->Header
.CurrentRecordNumber
= dwRecordsNumber
+ LogFile
->Header
.OldestRecordNumber
;
995 if (LogFile
->Header
.CurrentRecordNumber
== 0)
996 LogFile
->Header
.CurrentRecordNumber
= 1;
1000 FileOffset
.QuadPart
= 0LL;
1001 Status
= NtWriteFile(LogFile
->hFile
,
1007 sizeof(EVENTLOGHEADER
),
1010 if (!NT_SUCCESS(Status
))
1012 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1013 return STATUS_EVENTLOG_FILE_CORRUPT
;
1016 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1017 if (!NT_SUCCESS(Status
))
1019 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
1020 return STATUS_EVENTLOG_FILE_CORRUPT
;
1024 return STATUS_SUCCESS
;
1028 LogfCreate(PLOGFILE
* LogFile
,
1030 PUNICODE_STRING FileName
,
1036 NTSTATUS Status
= STATUS_SUCCESS
;
1037 OBJECT_ATTRIBUTES ObjectAttributes
;
1038 IO_STATUS_BLOCK IoStatusBlock
;
1041 BOOLEAN CreateNew
= FALSE
;
1043 pLogFile
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LOGFILE
));
1046 DPRINT1("Cannot allocate heap!\n");
1047 return STATUS_NO_MEMORY
;
1050 InitializeObjectAttributes(&ObjectAttributes
,
1052 OBJ_CASE_INSENSITIVE
,
1056 Status
= NtCreateFile(&pLogFile
->hFile
,
1057 Backup
? (GENERIC_READ
| SYNCHRONIZE
)
1058 : (GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
),
1062 FILE_ATTRIBUTE_NORMAL
,
1064 Backup
? FILE_OPEN
: FILE_OPEN_IF
,
1065 FILE_SYNCHRONOUS_IO_NONALERT
,
1068 if (!NT_SUCCESS(Status
))
1070 DPRINT1("Cannot create file %wZ (Status: 0x%08lx)\n", FileName
, Status
);
1074 CreateNew
= (IoStatusBlock
.Information
== FILE_CREATED
);
1076 LogNameLen
= (LogName
? wcslen(LogName
) : 0) + 1;
1077 pLogFile
->LogName
= RtlAllocateHeap(GetProcessHeap(),
1079 LogNameLen
* sizeof(WCHAR
));
1080 if (pLogFile
->LogName
== NULL
)
1082 DPRINT1("Cannot allocate heap\n");
1083 Status
= STATUS_NO_MEMORY
;
1088 StringCchCopyW(pLogFile
->LogName
, LogNameLen
, LogName
);
1090 pLogFile
->FileName
= RtlAllocateHeap(GetProcessHeap(),
1092 /*(wcslen(FileName->Buffer) + 1) * sizeof(WCHAR)*/
1093 FileName
->Length
+ sizeof(UNICODE_NULL
));
1094 if (pLogFile
->FileName
== NULL
)
1096 DPRINT1("Cannot allocate heap\n");
1097 Status
= STATUS_NO_MEMORY
;
1101 StringCchCopyW(pLogFile
->FileName
,
1102 /*wcslen(FileName->Buffer) + 1*/ (FileName
->Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
),
1105 pLogFile
->OffsetInfo
= RtlAllocateHeap(GetProcessHeap(),
1107 sizeof(EVENT_OFFSET_INFO
) * 64);
1108 if (pLogFile
->OffsetInfo
== NULL
)
1110 DPRINT1("Cannot allocate heap\n");
1111 Status
= STATUS_NO_MEMORY
;
1114 pLogFile
->OffsetInfoSize
= 64;
1115 pLogFile
->OffsetInfoNext
= 0;
1117 pLogFile
->Permanent
= Permanent
;
1119 // FIXME: Always use the regitry values for MaxSize & Retention,
1120 // even for existing logs!
1122 // FIXME: On Windows, EventLog uses the MaxSize setting
1123 // from the registry itself; the MaxSize from the header
1124 // is just for information purposes.
1127 Status
= LogfInitializeNew(pLogFile
, ulMaxSize
, ulRetention
);
1129 Status
= LogfInitializeExisting(pLogFile
, Backup
);
1131 if (!NT_SUCCESS(Status
))
1134 RtlInitializeResource(&pLogFile
->Lock
);
1136 LogfListAddItem(pLogFile
);
1139 if (!NT_SUCCESS(Status
))
1141 if ((pLogFile
->hFile
!= NULL
) && (pLogFile
->hFile
!= INVALID_HANDLE_VALUE
))
1142 NtClose(pLogFile
->hFile
);
1144 if (pLogFile
->OffsetInfo
)
1145 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->OffsetInfo
);
1147 if (pLogFile
->FileName
)
1148 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->FileName
);
1150 if (pLogFile
->LogName
)
1151 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
->LogName
);
1153 RtlFreeHeap(GetProcessHeap(), 0, pLogFile
);
1157 *LogFile
= pLogFile
;
1164 LogfClose(PLOGFILE LogFile
,
1167 IO_STATUS_BLOCK IoStatusBlock
;
1169 if (LogFile
== NULL
)
1172 if (!ForceClose
&& LogFile
->Permanent
)
1175 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1177 NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1178 NtClose(LogFile
->hFile
);
1179 LogfListRemoveItem(LogFile
);
1181 RtlDeleteResource(&LogFile
->Lock
);
1183 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->LogName
);
1184 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->FileName
);
1185 RtlFreeHeap(GetProcessHeap(), 0, LogFile
->OffsetInfo
);
1186 RtlFreeHeap(GetProcessHeap(), 0, LogFile
);
1193 ReadAnsiLogEntry(IN PLOGFILE LogFile
,
1194 OUT PIO_STATUS_BLOCK IoStatusBlock
,
1197 IN PLARGE_INTEGER ByteOffset
,
1198 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
1201 PVOID UnicodeBuffer
= NULL
;
1202 PEVENTLOGRECORD Src
, Dst
;
1203 ANSI_STRING StringA
;
1204 UNICODE_STRING StringW
;
1205 PVOID SrcPtr
, DstPtr
;
1209 DWORD dwEntryLength
;
1212 IoStatusBlock
->Information
= 0;
1214 UnicodeBuffer
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, Length
);
1215 if (UnicodeBuffer
== NULL
)
1217 DPRINT1("Alloc failed!\n");
1218 return STATUS_NO_MEMORY
;
1221 Status
= ReadLogBuffer(LogFile
,
1227 if (!NT_SUCCESS(Status
))
1229 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1230 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1233 // dwRead = IoStatusBlock->Information;
1235 Src
= (PEVENTLOGRECORD
)UnicodeBuffer
;
1236 Dst
= (PEVENTLOGRECORD
)Buffer
;
1238 Dst
->Reserved
= Src
->Reserved
;
1239 Dst
->RecordNumber
= Src
->RecordNumber
;
1240 Dst
->TimeGenerated
= Src
->TimeGenerated
;
1241 Dst
->TimeWritten
= Src
->TimeWritten
;
1242 Dst
->EventID
= Src
->EventID
;
1243 Dst
->EventType
= Src
->EventType
;
1244 Dst
->EventCategory
= Src
->EventCategory
;
1245 Dst
->NumStrings
= Src
->NumStrings
;
1246 Dst
->UserSidLength
= Src
->UserSidLength
;
1247 Dst
->DataLength
= Src
->DataLength
;
1249 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ sizeof(EVENTLOGRECORD
));
1250 DstPtr
= (PVOID
)((ULONG_PTR
)Dst
+ sizeof(EVENTLOGRECORD
));
1252 /* Convert the module name */
1253 RtlInitUnicodeString(&StringW
, SrcPtr
);
1254 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1255 if (NT_SUCCESS(Status
))
1257 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1258 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1260 RtlFreeAnsiString(&StringA
);
1264 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1265 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1267 SrcPtr
= (PVOID
)((ULONG_PTR
)SrcPtr
+ StringW
.MaximumLength
);
1269 /* Convert the computer name */
1270 RtlInitUnicodeString(&StringW
, SrcPtr
);
1271 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1272 if (NT_SUCCESS(Status
))
1274 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1275 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1277 RtlFreeAnsiString(&StringA
);
1281 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1282 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1285 /* Add the padding and the User SID */
1286 dwPadding
= sizeof(ULONG
) - (((ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
) % sizeof(ULONG
));
1287 RtlZeroMemory(DstPtr
, dwPadding
);
1289 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ Src
->UserSidOffset
);
1290 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ dwPadding
);
1292 Dst
->UserSidOffset
= (DWORD
)((ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
);
1293 RtlCopyMemory(DstPtr
, SrcPtr
, Src
->UserSidLength
);
1295 /* Convert the strings */
1296 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ Src
->StringOffset
);
1297 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ Src
->UserSidLength
);
1298 Dst
->StringOffset
= (DWORD
)((ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
);
1300 for (i
= 0; i
< Dst
->NumStrings
; i
++)
1302 RtlInitUnicodeString(&StringW
, SrcPtr
);
1303 Status
= RtlUnicodeStringToAnsiString(&StringA
, &StringW
, TRUE
);
1304 if (NT_SUCCESS(Status
))
1306 RtlCopyMemory(DstPtr
, StringA
.Buffer
, StringA
.MaximumLength
);
1307 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringA
.MaximumLength
);
1309 RtlFreeAnsiString(&StringA
);
1313 RtlZeroMemory(DstPtr
, StringW
.MaximumLength
/ sizeof(WCHAR
));
1314 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ StringW
.MaximumLength
/ sizeof(WCHAR
));
1316 SrcPtr
= (PVOID
)((ULONG_PTR
)SrcPtr
+ StringW
.MaximumLength
);
1319 /* Copy the binary data */
1320 SrcPtr
= (PVOID
)((ULONG_PTR
)Src
+ Src
->DataOffset
);
1321 Dst
->DataOffset
= (ULONG_PTR
)DstPtr
- (ULONG_PTR
)Dst
;
1322 RtlCopyMemory(DstPtr
, SrcPtr
, Src
->DataLength
);
1323 DstPtr
= (PVOID
)((ULONG_PTR
)DstPtr
+ Src
->DataLength
);
1325 /* Add the padding */
1326 dwPadding
= sizeof(ULONG
) - (((ULONG_PTR
)DstPtr
-(ULONG_PTR
)Dst
) % sizeof(ULONG
));
1327 RtlZeroMemory(DstPtr
, dwPadding
);
1329 dwEntryLength
= (DWORD
)((ULONG_PTR
)DstPtr
+ dwPadding
+ sizeof(ULONG
) - (ULONG_PTR
)Dst
);
1331 /* Set the entry length at the end of the entry */
1332 pLength
= (PDWORD
)((ULONG_PTR
)DstPtr
+ dwPadding
);
1333 *pLength
= dwEntryLength
;
1334 Dst
->Length
= dwEntryLength
;
1336 IoStatusBlock
->Information
= dwEntryLength
;
1338 Status
= STATUS_SUCCESS
;
1341 if (UnicodeBuffer
!= NULL
)
1342 RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer
);
1349 * 'RecordNumber' is a pointer to the record number at which the read operation
1350 * should start. If the record number is 0 and the flags given in the 'Flags'
1351 * parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is
1355 LogfReadEvents(PLOGFILE LogFile
,
1357 PULONG RecordNumber
,
1365 IO_STATUS_BLOCK IoStatusBlock
;
1366 LARGE_INTEGER FileOffset
;
1367 DWORD dwOffset
, dwRead
, dwRecSize
;
1368 DWORD dwBufferUsage
, dwRecNum
;
1370 /* Parameters validation */
1372 /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */
1373 if ((Flags
& EVENTLOG_SEQUENTIAL_READ
) && (Flags
& EVENTLOG_SEEK_READ
))
1374 return STATUS_INVALID_PARAMETER
;
1376 if (!(Flags
& EVENTLOG_SEQUENTIAL_READ
) && !(Flags
& EVENTLOG_SEEK_READ
))
1377 return STATUS_INVALID_PARAMETER
;
1379 /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */
1380 if ((Flags
& EVENTLOG_FORWARDS_READ
) && (Flags
& EVENTLOG_BACKWARDS_READ
))
1381 return STATUS_INVALID_PARAMETER
;
1383 if (!(Flags
& EVENTLOG_FORWARDS_READ
) && !(Flags
& EVENTLOG_BACKWARDS_READ
))
1384 return STATUS_INVALID_PARAMETER
;
1386 if (!Buffer
|| !BytesRead
|| !BytesNeeded
)
1387 return STATUS_INVALID_PARAMETER
;
1389 /* In seek read mode, a record number of 0 is invalid */
1390 if (!(Flags
& EVENTLOG_SEQUENTIAL_READ
) && (*RecordNumber
== 0))
1391 return STATUS_INVALID_PARAMETER
;
1394 * In sequential read mode, a record number of 0 means we need
1395 * to determine where to start the read operation. Otherwise
1396 * we just use the provided record number.
1398 if ((Flags
& EVENTLOG_SEQUENTIAL_READ
) && (*RecordNumber
== 0))
1400 if (Flags
& EVENTLOG_FORWARDS_READ
)
1402 *RecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1404 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1406 *RecordNumber
= LogFile
->Header
.CurrentRecordNumber
- 1;
1410 dwRecNum
= *RecordNumber
;
1412 RtlAcquireResourceShared(&LogFile
->Lock
, TRUE
);
1420 dwOffset
= LogfOffsetByNumber(LogFile
, dwRecNum
);
1423 if (dwBufferUsage
== 0)
1425 RtlReleaseResource(&LogFile
->Lock
);
1426 return STATUS_END_OF_FILE
;
1434 FileOffset
.QuadPart
= dwOffset
;
1435 Status
= NtReadFile(LogFile
->hFile
,
1444 if (!NT_SUCCESS(Status
))
1446 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
1447 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1450 // dwRead = IoStatusBlock.Information;
1452 if (dwBufferUsage
+ dwRecSize
> BufSize
)
1454 if (dwBufferUsage
== 0)
1456 *BytesNeeded
= dwRecSize
;
1457 RtlReleaseResource(&LogFile
->Lock
);
1458 return STATUS_BUFFER_TOO_SMALL
;
1466 FileOffset
.QuadPart
= dwOffset
;
1469 Status
= ReadAnsiLogEntry(LogFile
,
1471 Buffer
+ dwBufferUsage
,
1475 if (!NT_SUCCESS(Status
))
1477 DPRINT1("ReadAnsiLogEntry failed (Status 0x%08lx)\n", Status
);
1478 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1484 Status
= ReadLogBuffer(LogFile
,
1486 Buffer
+ dwBufferUsage
,
1490 if (!NT_SUCCESS(Status
))
1492 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1493 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1497 dwRead
= IoStatusBlock
.Information
;
1499 /* Go to the next event record */
1501 * NOTE: This implicitely supposes that all the other record numbers
1502 * are consecutive (and do not jump than more than one unit); but if
1503 * it is not the case, then we would prefer here to call some
1504 * "get_next_record_number" function.
1506 if (Flags
& EVENTLOG_FORWARDS_READ
)
1508 else // if (Flags & EVENTLOG_BACKWARDS_READ)
1511 dwBufferUsage
+= dwRead
;
1513 while (dwBufferUsage
<= BufSize
);
1515 *BytesRead
= dwBufferUsage
;
1516 *RecordNumber
= dwRecNum
;
1517 RtlReleaseResource(&LogFile
->Lock
);
1518 return STATUS_SUCCESS
;
1521 DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status
);
1522 RtlReleaseResource(&LogFile
->Lock
);
1527 LogfWriteRecord(PLOGFILE LogFile
,
1528 ULONG BufSize
, // SIZE_T
1529 PEVENTLOGRECORD Record
)
1532 IO_STATUS_BLOCK IoStatusBlock
;
1533 LARGE_INTEGER FileOffset
, NextOffset
;
1536 LARGE_INTEGER SystemTime
;
1538 EVENTLOGRECORD RecBuf
;
1539 ULONG FreeSpace
= 0;
1541 ULONG RecOffset
, WriteOffset
;
1543 // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1545 if (!Record
|| BufSize
< sizeof(*Record
))
1546 return STATUS_INVALID_PARAMETER
;
1548 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1551 * Retrieve the record written time now, that will also be compared
1552 * with the existing events timestamps in case the log is wrapping.
1554 NtQuerySystemTime(&SystemTime
);
1555 RtlTimeToSecondsSince1970(&SystemTime
, &Record
->TimeWritten
);
1557 Record
->RecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1559 /* Compute the available log free space */
1560 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
1561 FreeSpace
= LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
+ LogFile
->Header
.StartOffset
- sizeof(EVENTLOGHEADER
);
1562 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1563 FreeSpace
= LogFile
->Header
.StartOffset
- LogFile
->Header
.EndOffset
;
1565 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_DIRTY
;
1567 /* If the event log was empty, it will now contain one record */
1568 if (LogFile
->Header
.OldestRecordNumber
== 0)
1569 LogFile
->Header
.OldestRecordNumber
= 1;
1571 /* By default we append the new record at the old EOF record offset */
1572 WriteOffset
= LogFile
->Header
.EndOffset
;
1575 * Check whether the log is going to wrap (the events being overwritten).
1578 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
1579 UpperBound
= LogFile
->Header
.MaxSize
;
1580 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1581 UpperBound
= LogFile
->Header
.StartOffset
;
1583 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1584 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
1586 DPRINT("EventLogFile has reached maximum size (%x), wrapping...\n"
1587 "UpperBound = %x, WriteOffset = %x, BufSize = %x\n",
1588 LogFile
->Header
.MaxSize
, UpperBound
, WriteOffset
, BufSize
);
1589 /* This will be done later */
1592 if ( (LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
1593 (LogFile
->Header
.MaxSize
- WriteOffset
< sizeof(RecBuf
)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1595 // ASSERT(UpperBound == LogFile->Header.MaxSize);
1596 // ASSERT(WriteOffset == LogFile->Header.EndOffset);
1599 * We cannot fit the EVENTLOGRECORD header of the buffer before
1600 * the end of the file. We need to pad the end of the log with
1601 * 0x00000027, normally we will need to pad at most 0x37 bytes
1602 * (corresponding to sizeof(EVENTLOGRECORD) - 1).
1605 /* Rewind to the beginning of the log, just after the header */
1606 WriteOffset
= sizeof(EVENTLOGHEADER
);
1607 /**/UpperBound
= LogFile
->Header
.StartOffset
;/**/
1609 FreeSpace
= LogFile
->Header
.StartOffset
- WriteOffset
;
1611 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1614 * Otherwise, we can fit the header and only part
1615 * of the data will overwrite the oldest records.
1617 * It might be possible that all the event record can fit in one piece,
1618 * but that the EOF record needs to be split. This is not a problem,
1619 * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
1622 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
1624 ULONG OrgOldestRecordNumber
, OldestRecordNumber
;
1626 // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1628 OldestRecordNumber
= OrgOldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1630 // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
1631 // NOTE: It should be, by construction (and this should have been checked when
1632 // initializing a new, or existing log).
1635 * Determine how many old records need to be overwritten.
1636 * Check the size of the record as the record added may be larger.
1637 * Need to take into account that we append the EOF record.
1639 while (FreeSpace
< BufSize
+ sizeof(EofRec
))
1641 /* Get the oldest record data */
1642 RecOffset
= LogfOffsetByNumber(LogFile
, OldestRecordNumber
);
1645 // TODO: It cannot, queue a message box for the user and exit.
1646 // See also below...
1647 DPRINT1("Record number %d cannot be found, or LogFile is full and cannot wrap!\n", OldestRecordNumber
);
1648 Status
= STATUS_LOG_FILE_FULL
; // STATUS_LOG_FULL;
1652 RtlZeroMemory(&RecBuf
, sizeof(RecBuf
));
1654 FileOffset
.QuadPart
= RecOffset
;
1655 Status
= NtReadFile(LogFile
->hFile
,
1664 if (!NT_SUCCESS(Status
))
1666 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
1667 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1670 // dwRead = IoStatusBlock.Information;
1672 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
)
1674 DPRINT1("LogFile corrupt!\n");
1675 Status
= STATUS_EVENTLOG_FILE_CORRUPT
;
1680 * Check whether this event can be overwritten by comparing its
1681 * written timestamp with the log's retention value. This value
1682 * is the time interval, in seconds, that events records are
1683 * protected from being overwritten.
1685 * If the retention value is zero the events are always overwritten.
1687 * If the retention value is non-zero, when the age of an event,
1688 * in seconds, reaches or exceeds this value, it can be overwritten.
1689 * Also if the events are in the future, we do not overwrite them.
1691 if (LogFile
->Header
.Retention
!= 0 &&
1692 (Record
->TimeWritten
< RecBuf
.TimeWritten
||
1693 (Record
->TimeWritten
>= RecBuf
.TimeWritten
&&
1694 Record
->TimeWritten
- RecBuf
.TimeWritten
< LogFile
->Header
.Retention
)))
1696 // TODO: It cannot, queue a message box for the user and exit.
1697 DPRINT1("LogFile is full and cannot wrap!\n");
1698 Status
= STATUS_LOG_FILE_FULL
; // STATUS_LOG_FULL;
1703 * Advance the oldest record number, add the event record length
1704 * (as long as it is valid...) then take account for the possible
1705 * paddind after the record, in case this is the last one at the
1708 OldestRecordNumber
++;
1709 RecOffset
+= RecBuf
.Length
;
1710 FreeSpace
+= RecBuf
.Length
;
1713 * If this was the last event record before the end of the log file,
1714 * the next one should start at the beginning of the log and the space
1715 * between the last event record and the end of the file is padded.
1717 if (LogFile
->Header
.MaxSize
- RecOffset
< sizeof(EVENTLOGRECORD
))
1719 /* Add the padding size */
1720 FreeSpace
+= LogFile
->Header
.MaxSize
- RecOffset
;
1724 DPRINT("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace
, BufSize
);
1726 /* The log records are wrapping */
1727 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1730 // FIXME: May lead to corruption if the other subsequent calls fail...
1733 * We have validated all the region of events to be discarded,
1734 * now we can perform their deletion.
1736 LogfDeleteOffsetInformation(LogFile
, OrgOldestRecordNumber
, OldestRecordNumber
- 1);
1737 LogFile
->Header
.OldestRecordNumber
= OldestRecordNumber
;
1738 LogFile
->Header
.StartOffset
= LogfOffsetByNumber(LogFile
, OldestRecordNumber
);
1739 if (LogFile
->Header
.StartOffset
== 0)
1742 * We have deleted all the existing event records to make place
1743 * for the new one. We can put it at the start of the event log.
1745 LogFile
->Header
.StartOffset
= sizeof(EVENTLOGHEADER
);
1746 WriteOffset
= LogFile
->Header
.StartOffset
;
1747 LogFile
->Header
.EndOffset
= WriteOffset
;
1750 DPRINT1("MaxSize = %x, StartOffset = %x, WriteOffset = %x, EndOffset = %x, BufSize = %x\n"
1751 "OldestRecordNumber = %d\n",
1752 LogFile
->Header
.MaxSize
, LogFile
->Header
.StartOffset
, WriteOffset
, LogFile
->Header
.EndOffset
, BufSize
,
1753 OldestRecordNumber
);
1757 * Expand the log file if needed.
1758 * NOTE: It may be needed to perform this task a bit sooner if we need
1759 * such a thing for performing read operations, in the future...
1760 * Or if this operation needs to modify 'FreeSpace'...
1762 if (LogFile
->CurrentSize
< LogFile
->Header
.MaxSize
)
1764 DPRINT1("Expanding the log file from %lu to %lu\n",
1765 LogFile
->CurrentSize
, LogFile
->Header
.MaxSize
);
1767 /* For the moment this is a trivial operation */
1768 LogFile
->CurrentSize
= LogFile
->Header
.MaxSize
;
1771 /* Pad the end of the log */
1772 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1773 if (WriteOffset
< LogFile
->Header
.EndOffset
)
1775 /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
1776 dwWritten
= ROUND_DOWN(LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
, sizeof(ULONG
));
1777 RtlFillMemoryUlong(&RecBuf
, dwWritten
, 0x00000027);
1779 FileOffset
.QuadPart
= LogFile
->Header
.EndOffset
;
1780 Status
= NtWriteFile(LogFile
->hFile
,
1789 // dwWritten = IoStatusBlock.Information;
1790 if (!NT_SUCCESS(Status
))
1792 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1797 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1798 FileOffset
.QuadPart
= WriteOffset
;
1799 Status
= WriteLogBuffer(LogFile
,
1805 // dwWritten = IoStatusBlock.Information;
1806 if (!NT_SUCCESS(Status
))
1808 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1811 /* FileOffset now contains the offset just after the end of the record buffer */
1812 FileOffset
= NextOffset
;
1814 if (!LogfAddOffsetInformation(LogFile
,
1815 Record
->RecordNumber
,
1818 Status
= STATUS_NO_MEMORY
; // STATUS_EVENTLOG_FILE_CORRUPT;
1822 LogFile
->Header
.CurrentRecordNumber
++;
1823 if (LogFile
->Header
.CurrentRecordNumber
== 0)
1824 LogFile
->Header
.CurrentRecordNumber
= 1;
1827 * Write the new EOF record offset just after the event record.
1828 * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
1829 * bytes remains between the end of the record and the end of the log file.
1831 LogFile
->Header
.EndOffset
= FileOffset
.QuadPart
;
1833 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
1834 EofRec
.BeginRecord
= LogFile
->Header
.StartOffset
;
1835 EofRec
.EndRecord
= LogFile
->Header
.EndOffset
;
1836 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1837 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1839 // FileOffset.QuadPart = LogFile->Header.EndOffset;
1840 Status
= WriteLogBuffer(LogFile
,
1846 // dwWritten = IoStatusBlock.Information;
1847 if (!NT_SUCCESS(Status
))
1849 DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1852 FileOffset
= NextOffset
;
1854 LogFile
->Header
.Flags
&= ELF_LOGFILE_HEADER_DIRTY
;
1856 /* Update the event log header */
1857 FileOffset
.QuadPart
= 0LL;
1858 Status
= NtWriteFile(LogFile
->hFile
,
1864 sizeof(EVENTLOGHEADER
),
1867 // dwWritten = IoStatusBlock.Information;
1868 if (!NT_SUCCESS(Status
))
1870 DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status
);
1874 Status
= NtFlushBuffersFile(LogFile
->hFile
, &IoStatusBlock
);
1875 if (!NT_SUCCESS(Status
))
1877 DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status
);
1881 Status
= STATUS_SUCCESS
;
1884 RtlReleaseResource(&LogFile
->Lock
);
1889 LogfClearFile(PLOGFILE LogFile
,
1890 PUNICODE_STRING BackupFileName
)
1894 RtlAcquireResourceExclusive(&LogFile
->Lock
, TRUE
);
1896 if (BackupFileName
->Length
> 0)
1898 /* Write a backup file */
1899 Status
= LogfBackupFile(LogFile
, BackupFileName
);
1900 if (!NT_SUCCESS(Status
))
1902 DPRINT1("LogfBackupFile failed (Status: 0x%08lx)\n", Status
);
1907 Status
= LogfInitializeNew(LogFile
,
1908 LogFile
->Header
.MaxSize
,
1909 LogFile
->Header
.Retention
);
1910 if (!NT_SUCCESS(Status
))
1912 DPRINT1("LogfInitializeNew failed (Status: 0x%08lx)\n", Status
);
1916 RtlReleaseResource(&LogFile
->Lock
);
1921 LogfBackupFile(PLOGFILE LogFile
,
1922 PUNICODE_STRING BackupFileName
)
1925 OBJECT_ATTRIBUTES ObjectAttributes
;
1926 IO_STATUS_BLOCK IoStatusBlock
;
1927 LARGE_INTEGER FileOffset
;
1928 HANDLE FileHandle
= NULL
;
1929 EVENTLOGHEADER Header
;
1930 EVENTLOGRECORD RecBuf
;
1934 PVOID Buffer
= NULL
;
1938 DPRINT1("LogfBackupFile(%p, %wZ)\n", LogFile
, BackupFileName
);
1940 /* Lock the log file shared */
1941 RtlAcquireResourceShared(&LogFile
->Lock
, TRUE
);
1943 InitializeObjectAttributes(&ObjectAttributes
,
1945 OBJ_CASE_INSENSITIVE
,
1949 Status
= NtCreateFile(&FileHandle
,
1950 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
1954 FILE_ATTRIBUTE_NORMAL
,
1957 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
1960 if (!NT_SUCCESS(Status
))
1962 DPRINT("Cannot create backup file %wZ (Status: 0x%08lx)\n", BackupFileName
, Status
);
1966 /* Initialize the (dirty) log file header */
1967 Header
.HeaderSize
= sizeof(Header
);
1968 Header
.Signature
= LOGFILE_SIGNATURE
;
1969 Header
.MajorVersion
= MAJORVER
;
1970 Header
.MinorVersion
= MINORVER
;
1971 Header
.StartOffset
= sizeof(Header
);
1972 Header
.EndOffset
= sizeof(Header
);
1973 Header
.CurrentRecordNumber
= 1;
1974 Header
.OldestRecordNumber
= 0;
1975 Header
.MaxSize
= LogFile
->Header
.MaxSize
;
1976 Header
.Flags
= ELF_LOGFILE_HEADER_DIRTY
;
1977 Header
.Retention
= LogFile
->Header
.Retention
;
1978 Header
.EndHeaderSize
= sizeof(Header
);
1980 /* Write the (dirty) log file header */
1981 FileOffset
.QuadPart
= 0LL;
1982 Status
= NtWriteFile(FileHandle
,
1991 if (!NT_SUCCESS(Status
))
1993 DPRINT1("Failed to write the log file header (Status: 0x%08lx)\n", Status
);
1997 for (i
= LogFile
->Header
.OldestRecordNumber
; i
< LogFile
->Header
.CurrentRecordNumber
; i
++)
1999 RecOffset
= LogfOffsetByNumber(LogFile
, i
);
2003 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
2004 FileOffset
.QuadPart
= RecOffset
;
2005 Status
= NtReadFile(LogFile
->hFile
,
2014 if (!NT_SUCCESS(Status
))
2016 DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status
);
2019 // dwRead = IoStatusBlock.Information;
2021 // if (dwRead != sizeof(RecBuf))
2024 Buffer
= RtlAllocateHeap(GetProcessHeap(), 0, RecBuf
.Length
);
2027 DPRINT1("RtlAllocateHeap() failed!\n");
2031 /* Read the full EVENTLOGRECORD (header + data) with wrapping */
2032 Status
= ReadLogBuffer(LogFile
,
2038 if (!NT_SUCCESS(Status
))
2040 DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
2041 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
2042 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
2045 // dwRead = IoStatusBlock.Information;
2047 /* Write the event record (no wrap for the backup log) */
2048 Status
= NtWriteFile(FileHandle
,
2057 if (!NT_SUCCESS(Status
))
2059 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status
);
2060 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
2064 /* Update the header information */
2065 Header
.EndOffset
+= RecBuf
.Length
;
2067 /* Free the buffer */
2068 RtlFreeHeap(GetProcessHeap(), 0, Buffer
);
2072 /* Initialize the EOF record */
2073 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
2074 EofRec
.BeginRecord
= Header
.StartOffset
;
2075 EofRec
.EndRecord
= Header
.EndOffset
;
2076 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
2077 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
2079 /* Write the EOF record (no wrap for the backup log) */
2080 Status
= NtWriteFile(FileHandle
,
2089 if (!NT_SUCCESS(Status
))
2091 DPRINT1("NtWriteFile() failed!\n");
2095 /* Update the header information */
2096 Header
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
2097 Header
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
2098 Header
.MaxSize
= ROUND_UP(Header
.EndOffset
+ sizeof(EofRec
), sizeof(ULONG
));
2101 /* Write the (clean) log file header */
2102 FileOffset
.QuadPart
= 0LL;
2103 Status
= NtWriteFile(FileHandle
,
2112 if (!NT_SUCCESS(Status
))
2114 DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status
);
2118 /* Close the backup file */
2119 if (FileHandle
!= NULL
)
2120 NtClose(FileHandle
);
2122 /* Unlock the log file */
2123 RtlReleaseResource(&LogFile
->Lock
);
2130 LogfAllocAndBuildNewRecord(PSIZE_T pRecSize
,
2136 PCWSTR ComputerName
,
2146 PEVENTLOGRECORD pRec
;
2149 SIZE_T SourceNameLen
, ComputerNameLen
, StringLen
;
2151 SourceNameLen
= (SourceName
? wcslen(SourceName
) : 0) + 1;
2152 ComputerNameLen
= (ComputerName
? wcslen(ComputerName
) : 0) + 1;
2154 RecSize
= sizeof(EVENTLOGRECORD
) + (SourceNameLen
+ ComputerNameLen
) * sizeof(WCHAR
);
2156 /* Align on DWORD boundary for the SID */
2157 RecSize
= ROUND_UP(RecSize
, sizeof(ULONG
));
2159 RecSize
+= dwSidLength
;
2161 /* Add the sizes for the strings array */
2162 ASSERT((pStrings
== NULL
&& wNumStrings
== 0) ||
2163 (pStrings
!= NULL
&& wNumStrings
>= 0));
2164 for (i
= 0, str
= pStrings
; i
< wNumStrings
; i
++)
2166 StringLen
= wcslen(str
) + 1; // str must be != NULL
2167 RecSize
+= StringLen
* sizeof(WCHAR
);
2171 /* Add the data size */
2172 RecSize
+= dwDataSize
;
2174 /* Align on DWORD boundary for the full structure */
2175 RecSize
= ROUND_UP(RecSize
, sizeof(ULONG
));
2177 /* Size of the trailing 'Length' member */
2178 RecSize
+= sizeof(ULONG
);
2180 Buffer
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, RecSize
);
2183 DPRINT1("Cannot allocate heap!\n");
2187 pRec
= (PEVENTLOGRECORD
)Buffer
;
2188 pRec
->Length
= RecSize
;
2189 pRec
->Reserved
= LOGFILE_SIGNATURE
;
2192 * Do not assign here any precomputed record number to the event record.
2193 * The true record number will be assigned atomically and sequentially in
2194 * LogfWriteRecord, so that all the event records will have consistent and
2195 * unique record numbers.
2197 pRec
->RecordNumber
= 0;
2200 * Set the generated time, and temporarily set the written time
2201 * with the generated time.
2203 pRec
->TimeGenerated
= Time
;
2204 pRec
->TimeWritten
= Time
;
2206 pRec
->EventID
= dwEventId
;
2207 pRec
->EventType
= wType
;
2208 pRec
->EventCategory
= wCategory
;
2210 pos
= sizeof(EVENTLOGRECORD
);
2213 StringCchCopyW((PWSTR
)(Buffer
+ pos
), SourceNameLen
, SourceName
);
2214 pos
+= SourceNameLen
* sizeof(WCHAR
);
2216 StringCchCopyW((PWSTR
)(Buffer
+ pos
), ComputerNameLen
, ComputerName
);
2217 pos
+= ComputerNameLen
* sizeof(WCHAR
);
2219 /* Align on DWORD boundary for the SID */
2220 pos
= ROUND_UP(pos
, sizeof(ULONG
));
2222 pRec
->UserSidLength
= 0;
2223 pRec
->UserSidOffset
= 0;
2226 RtlCopyMemory(Buffer
+ pos
, pUserSid
, dwSidLength
);
2227 pRec
->UserSidLength
= dwSidLength
;
2228 pRec
->UserSidOffset
= pos
;
2232 pRec
->StringOffset
= pos
;
2233 for (i
= 0, str
= pStrings
; i
< wNumStrings
; i
++)
2235 StringLen
= wcslen(str
) + 1; // str must be != NULL
2236 StringCchCopyW((PWSTR
)(Buffer
+ pos
), StringLen
, str
);
2238 pos
+= StringLen
* sizeof(WCHAR
);
2240 pRec
->NumStrings
= wNumStrings
;
2242 pRec
->DataLength
= 0;
2243 pRec
->DataOffset
= 0;
2246 RtlCopyMemory(Buffer
+ pos
, pRawData
, dwDataSize
);
2247 pRec
->DataLength
= dwDataSize
;
2248 pRec
->DataOffset
= pos
;
2252 /* Align on DWORD boundary for the full structure */
2253 pos
= ROUND_UP(pos
, sizeof(ULONG
));
2255 /* Initialize the trailing 'Length' member */
2256 *((PDWORD
)(Buffer
+ pos
)) = RecSize
;
2258 *pRecSize
= RecSize
;
2263 LogfReportEvent(USHORT wType
,
2272 WCHAR szComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
2273 DWORD dwComputerNameLength
= MAX_COMPUTERNAME_LENGTH
+ 1;
2274 PEVENTLOGRECORD LogBuffer
;
2275 LARGE_INTEGER SystemTime
;
2279 if (!EventLogSource
)
2282 if (!GetComputerNameW(szComputerName
, &dwComputerNameLength
))
2284 szComputerName
[0] = L
'\0';
2287 NtQuerySystemTime(&SystemTime
);
2288 RtlTimeToSecondsSince1970(&SystemTime
, &Time
);
2290 LogBuffer
= LogfAllocAndBuildNewRecord(&RecSize
,
2295 EventLogSource
->szName
,
2304 Status
= LogfWriteRecord(EventLogSource
->LogFile
, RecSize
, LogBuffer
);
2305 if (!NT_SUCCESS(Status
))
2307 DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n",
2308 EventLogSource
->LogFile
->LogName
, Status
);
2311 LogfFreeRecord(LogBuffer
);