2 * PROJECT: ReactOS EventLog File Library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: sdk/lib/evtlib/evtlib.c
5 * PURPOSE: Provides functionality for reading and writing
6 * EventLog files in the NT <= 5.2 (.evt) format.
7 * PROGRAMMERS: Copyright 2005 Saveliy Tretiakov
12 /* INCLUDES ******************************************************************/
19 #define EVTLTRACE(...) DPRINT("EvtLib: " __VA_ARGS__)
20 // Once things become stabilized enough, replace all the EVTLTRACE1 by EVTLTRACE
21 #define EVTLTRACE1(...) DPRINT1("EvtLib: " __VA_ARGS__)
24 /* GLOBALS *******************************************************************/
26 static const EVENTLOGEOF EOFRecord
=
29 0x11111111, 0x22222222, 0x33333333, 0x44444444,
34 /* HELPER FUNCTIONS **********************************************************/
38 IN PEVTLOGFILE LogFile
,
41 OUT PSIZE_T ReadLength OPTIONAL
,
42 IN PLARGE_INTEGER ByteOffset
,
43 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
46 LARGE_INTEGER FileOffset
;
48 SIZE_T ReadBufLength
= 0, OldReadBufLength
;
50 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
51 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
57 NextOffset
->QuadPart
= 0LL;
59 /* Read the first part of the buffer */
60 FileOffset
= *ByteOffset
;
61 BufSize
= min(Length
, LogFile
->CurrentSize
- FileOffset
.QuadPart
);
63 Status
= LogFile
->FileRead(LogFile
,
68 if (!NT_SUCCESS(Status
))
70 EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status
);
76 OldReadBufLength
= ReadBufLength
;
79 * The buffer was splitted in two, its second part
80 * is to be read at the beginning of the log.
82 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
83 BufSize
= Length
- BufSize
;
84 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
86 Status
= LogFile
->FileRead(LogFile
,
91 if (!NT_SUCCESS(Status
))
93 EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status
);
96 /* Add the read number of bytes from the first read */
97 ReadBufLength
+= OldReadBufLength
;
101 *ReadLength
= ReadBufLength
;
103 /* We return the offset just after the end of the read buffer */
105 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
112 IN PEVTLOGFILE LogFile
,
115 OUT PSIZE_T WrittenLength OPTIONAL
,
116 IN PLARGE_INTEGER ByteOffset
,
117 OUT PLARGE_INTEGER NextOffset OPTIONAL
)
120 LARGE_INTEGER FileOffset
;
122 SIZE_T WrittenBufLength
= 0, OldWrittenBufLength
;
124 /* We must have write access to the log file */
125 ASSERT(!LogFile
->ReadOnly
);
128 * It is expected that the log file is already correctly expanded
129 * before we can write in it. Therefore the following assertions hold.
131 ASSERT(LogFile
->CurrentSize
<= LogFile
->Header
.MaxSize
);
132 ASSERT(ByteOffset
->QuadPart
<= LogFile
->CurrentSize
);
138 NextOffset
->QuadPart
= 0LL;
140 /* Write the first part of the buffer */
141 FileOffset
= *ByteOffset
;
142 BufSize
= min(Length
, LogFile
->CurrentSize
- FileOffset
.QuadPart
);
144 Status
= LogFile
->FileWrite(LogFile
,
149 if (!NT_SUCCESS(Status
))
151 EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status
);
155 if (Length
> BufSize
)
157 OldWrittenBufLength
= WrittenBufLength
;
160 * The buffer was splitted in two, its second part
161 * is written at the beginning of the log.
163 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BufSize
);
164 BufSize
= Length
- BufSize
;
165 FileOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
167 Status
= LogFile
->FileWrite(LogFile
,
172 if (!NT_SUCCESS(Status
))
174 EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status
);
177 /* Add the written number of bytes from the first write */
178 WrittenBufLength
+= OldWrittenBufLength
;
181 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
185 *WrittenLength
= WrittenBufLength
;
187 /* We return the offset just after the end of the written buffer */
189 NextOffset
->QuadPart
= FileOffset
.QuadPart
+ BufSize
;
195 /* Returns 0 if nothing is found */
198 IN PEVTLOGFILE LogFile
,
199 IN ULONG RecordNumber
)
203 for (i
= 0; i
< LogFile
->OffsetInfoNext
; i
++)
205 if (LogFile
->OffsetInfo
[i
].EventNumber
== RecordNumber
)
206 return LogFile
->OffsetInfo
[i
].EventOffset
;
211 #define OFFSET_INFO_INCREMENT 64
214 ElfpAddOffsetInformation(
215 IN PEVTLOGFILE LogFile
,
221 if (LogFile
->OffsetInfoNext
== LogFile
->OffsetInfoSize
)
223 /* Allocate a new offset table */
224 NewOffsetInfo
= LogFile
->Allocate((LogFile
->OffsetInfoSize
+ OFFSET_INFO_INCREMENT
) *
225 sizeof(EVENT_OFFSET_INFO
),
230 EVTLTRACE1("Cannot reallocate heap.\n");
234 /* Free the old offset table and use the new one */
235 if (LogFile
->OffsetInfo
)
237 /* Copy the handles from the old table to the new one */
238 RtlCopyMemory(NewOffsetInfo
,
240 LogFile
->OffsetInfoSize
* sizeof(EVENT_OFFSET_INFO
));
241 LogFile
->Free(LogFile
->OffsetInfo
, 0, TAG_ELF
);
243 LogFile
->OffsetInfo
= (PEVENT_OFFSET_INFO
)NewOffsetInfo
;
244 LogFile
->OffsetInfoSize
+= OFFSET_INFO_INCREMENT
;
247 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventNumber
= ulNumber
;
248 LogFile
->OffsetInfo
[LogFile
->OffsetInfoNext
].EventOffset
= ulOffset
;
249 LogFile
->OffsetInfoNext
++;
255 ElfpDeleteOffsetInformation(
256 IN PEVTLOGFILE LogFile
,
257 IN ULONG ulNumberMin
,
258 IN ULONG ulNumberMax
)
262 if (ulNumberMin
> ulNumberMax
)
265 /* Remove records ulNumberMin to ulNumberMax inclusive */
266 while (ulNumberMin
<= ulNumberMax
)
269 * As the offset information is listed in increasing order, and we want
270 * to keep the list without holes, we demand that ulNumberMin is the first
271 * element in the list.
273 if (ulNumberMin
!= LogFile
->OffsetInfo
[0].EventNumber
)
277 * RtlMoveMemory(&LogFile->OffsetInfo[0],
278 * &LogFile->OffsetInfo[1],
279 * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
281 for (i
= 0; i
< LogFile
->OffsetInfoNext
- 1; i
++)
283 LogFile
->OffsetInfo
[i
].EventNumber
= LogFile
->OffsetInfo
[i
+ 1].EventNumber
;
284 LogFile
->OffsetInfo
[i
].EventOffset
= LogFile
->OffsetInfo
[i
+ 1].EventOffset
;
286 LogFile
->OffsetInfoNext
--;
288 /* Go to the next offset information */
298 IN PEVTLOGFILE LogFile
,
304 LARGE_INTEGER FileOffset
;
305 SIZE_T WrittenLength
;
308 /* Initialize the event log header */
309 RtlZeroMemory(&LogFile
->Header
, sizeof(EVENTLOGHEADER
));
311 LogFile
->Header
.HeaderSize
= sizeof(EVENTLOGHEADER
);
312 LogFile
->Header
.Signature
= LOGFILE_SIGNATURE
;
313 LogFile
->Header
.MajorVersion
= MAJORVER
;
314 LogFile
->Header
.MinorVersion
= MINORVER
;
316 /* Set the offset to the oldest record */
317 LogFile
->Header
.StartOffset
= sizeof(EVENTLOGHEADER
);
318 /* Set the offset to the ELF_EOF_RECORD */
319 LogFile
->Header
.EndOffset
= sizeof(EVENTLOGHEADER
);
320 /* Set the number of the next record that will be added */
321 LogFile
->Header
.CurrentRecordNumber
= 1;
322 /* The event log is empty, there is no record so far */
323 LogFile
->Header
.OldestRecordNumber
= 0;
325 // FIXME: Windows' EventLog log file sizes are always multiple of 64kB
326 // but that does not mean the real log size is == file size.
328 /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */
329 LogFile
->Header
.MaxSize
= ROUND_UP(MaxSize
, sizeof(ULONG
));
330 LogFile
->CurrentSize
= LogFile
->Header
.MaxSize
; // or: FileSize ??
331 LogFile
->FileSetSize(LogFile
, LogFile
->CurrentSize
, 0);
333 LogFile
->Header
.Flags
= 0;
334 LogFile
->Header
.Retention
= Retention
;
335 LogFile
->Header
.EndHeaderSize
= sizeof(EVENTLOGHEADER
);
337 /* Write the header */
338 FileOffset
.QuadPart
= 0LL;
339 Status
= LogFile
->FileWrite(LogFile
,
342 sizeof(EVENTLOGHEADER
),
344 if (!NT_SUCCESS(Status
))
346 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
350 /* Initialize the ELF_EOF_RECORD and write it */
351 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
352 EofRec
.BeginRecord
= LogFile
->Header
.StartOffset
;
353 EofRec
.EndRecord
= LogFile
->Header
.EndOffset
;
354 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
355 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
357 Status
= LogFile
->FileWrite(LogFile
,
362 if (!NT_SUCCESS(Status
))
364 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
368 Status
= LogFile
->FileFlush(LogFile
, NULL
, 0);
369 if (!NT_SUCCESS(Status
))
371 EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status
);
375 return STATUS_SUCCESS
;
379 ElfpInitExistingFile(
380 IN PEVTLOGFILE LogFile
,
386 LARGE_INTEGER FileOffset
, NextOffset
;
388 ULONG RecordNumber
= 0;
392 EVENTLOGRECORD RecBuf
;
393 PEVENTLOGRECORD pRecBuf
;
394 BOOLEAN Wrapping
= FALSE
;
395 BOOLEAN IsLogDirty
= FALSE
;
397 /* Read the log header */
398 FileOffset
.QuadPart
= 0LL;
399 Status
= LogFile
->FileRead(LogFile
,
402 sizeof(EVENTLOGHEADER
),
404 if (!NT_SUCCESS(Status
))
406 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status
);
407 return STATUS_EVENTLOG_FILE_CORRUPT
; // return Status;
409 if (ReadLength
!= sizeof(EVENTLOGHEADER
))
411 EVTLTRACE("Invalid file `%wZ'.\n", &LogFile
->FileName
);
412 return STATUS_EVENTLOG_FILE_CORRUPT
;
415 /* Header validity checks */
417 if (LogFile
->Header
.HeaderSize
!= sizeof(EVENTLOGHEADER
) ||
418 LogFile
->Header
.EndHeaderSize
!= sizeof(EVENTLOGHEADER
))
420 EVTLTRACE("Invalid header size in `%wZ'.\n", &LogFile
->FileName
);
421 return STATUS_EVENTLOG_FILE_CORRUPT
;
424 if (LogFile
->Header
.Signature
!= LOGFILE_SIGNATURE
)
426 EVTLTRACE("Invalid signature %x in `%wZ'.\n",
427 LogFile
->Header
.Signature
, &LogFile
->FileName
);
428 return STATUS_EVENTLOG_FILE_CORRUPT
;
431 IsLogDirty
= (LogFile
->Header
.Flags
& ELF_LOGFILE_HEADER_DIRTY
);
433 /* If the log is read-only (e.g. a backup log) and is dirty, then it is corrupted */
434 if (LogFile
->ReadOnly
&& IsLogDirty
)
436 EVTLTRACE("Read-only log `%wZ' is dirty.\n", &LogFile
->FileName
);
437 return STATUS_EVENTLOG_FILE_CORRUPT
;
440 LogFile
->CurrentSize
= FileSize
;
441 // FIXME!! What to do? And what to do if the MaxSize from the registry
442 // is strictly less than the CurrentSize?? Should we "reduce" the log size
443 // by clearing it completely??
444 // --> ANSWER: Save the new MaxSize somewhere, and only when the log is
445 // being cleared, use the new MaxSize to resize (ie. shrink) it.
446 // LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
448 /* Adjust the log maximum size if needed */
449 if (LogFile
->CurrentSize
> LogFile
->Header
.MaxSize
)
450 LogFile
->Header
.MaxSize
= LogFile
->CurrentSize
;
453 * Reset the log retention value. The value stored
454 * in the log file is just for information purposes.
456 LogFile
->Header
.Retention
= Retention
;
459 * For a non-read-only dirty log, the most up-to-date information about
460 * the Start/End offsets and the Oldest and Current event record numbers
461 * are found in the EOF record. We need to locate the EOF record without
462 * relying on the log header's EndOffset, then patch the log header with
463 * the values from the EOF record.
465 if ((LogFile
->Header
.EndOffset
>= sizeof(EVENTLOGHEADER
)) &&
466 (LogFile
->Header
.EndOffset
< LogFile
->CurrentSize
) &&
467 (LogFile
->Header
.EndOffset
& 3) == 0) // EndOffset % sizeof(ULONG) == 0
469 /* The header EOF offset may be valid, try to start with it */
470 RecOffset
= LogFile
->Header
.EndOffset
;
474 /* The header EOF offset could not be valid, so start from the beginning */
475 RecOffset
= sizeof(EVENTLOGHEADER
);
478 FileOffset
.QuadPart
= RecOffset
;
483 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
485 EVTLTRACE1("EOF record not found!\n");
486 return STATUS_EVENTLOG_FILE_CORRUPT
;
489 /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
490 Status
= ReadLogBuffer(LogFile
,
492 EVENTLOGEOF_SIZE_FIXED
,
496 if (!NT_SUCCESS(Status
))
498 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
499 return STATUS_EVENTLOG_FILE_CORRUPT
;
501 if (ReadLength
!= EVENTLOGEOF_SIZE_FIXED
)
503 EVTLTRACE1("Cannot read at most an EOF record!\n");
504 return STATUS_EVENTLOG_FILE_CORRUPT
;
507 /* Is it an EVENTLOGEOF record? */
508 if (RtlCompareMemory(&EofRec
, &EOFRecord
, EVENTLOGEOF_SIZE_FIXED
) == EVENTLOGEOF_SIZE_FIXED
)
510 DPRINT1("Found EOF record at %llx\n", FileOffset
.QuadPart
);
512 /* Got it! Break the loop and continue */
516 /* No, continue looping */
517 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
518 FileOffset
.QuadPart
+= sizeof(ULONG
);
520 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 2*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
521 FileOffset
.QuadPart
+= 2*sizeof(ULONG
);
523 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 3*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
524 FileOffset
.QuadPart
+= 3*sizeof(ULONG
);
526 if (*(PULONG
)((ULONG_PTR
)&EofRec
+ 4*sizeof(ULONG
)) == *(PULONG
)(&EOFRecord
))
527 FileOffset
.QuadPart
+= 4*sizeof(ULONG
);
529 FileOffset
.QuadPart
+= 5*sizeof(ULONG
); // EVENTLOGEOF_SIZE_FIXED
531 if (FileOffset
.QuadPart
>= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */)
533 /* Wrap the offset */
534 FileOffset
.QuadPart
-= LogFile
->CurrentSize
/* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER
);
539 * The only way to be there is to have found a valid EOF record.
540 * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT
544 /* Read the full EVENTLOGEOF (may wrap) and validate it */
545 Status
= ReadLogBuffer(LogFile
,
551 if (!NT_SUCCESS(Status
))
553 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
554 return STATUS_EVENTLOG_FILE_CORRUPT
;
556 if (ReadLength
!= sizeof(EofRec
))
558 EVTLTRACE1("Cannot read the full EOF record!\n");
559 return STATUS_EVENTLOG_FILE_CORRUPT
;
562 /* Complete validity checks */
563 if ((EofRec
.RecordSizeEnd
!= EofRec
.RecordSizeBeginning
) ||
564 (EofRec
.EndRecord
!= FileOffset
.QuadPart
))
566 DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected 0x%x 0x%x!\n",
568 EofRec
.RecordSizeEnd
, EofRec
.RecordSizeBeginning
,
569 EofRec
.EndRecord
, FileOffset
.QuadPart
,
570 EOFRecord
.RecordSizeEnd
, EOFRecord
.RecordSizeBeginning
);
571 DPRINT1("RecordSizeEnd = 0x%x\n", EofRec
.RecordSizeEnd
);
572 DPRINT1("RecordSizeBeginning = 0x%x\n", EofRec
.RecordSizeBeginning
);
573 DPRINT1("EndRecord = 0x%x\n", EofRec
.EndRecord
);
574 return STATUS_EVENTLOG_FILE_CORRUPT
;
577 /* The EOF record is valid, break the loop and continue */
579 /* If the log is not dirty, the header values should correspond to the EOF ones */
582 if ( (LogFile
->Header
.StartOffset
!= EofRec
.BeginRecord
) ||
583 (LogFile
->Header
.EndOffset
!= EofRec
.EndRecord
) ||
584 (LogFile
->Header
.CurrentRecordNumber
!= EofRec
.CurrentRecordNumber
) ||
585 (LogFile
->Header
.OldestRecordNumber
!= EofRec
.OldestRecordNumber
) )
588 "Log header or EOF record is corrupted:\n"
589 " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n"
590 " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n",
591 LogFile
->Header
.StartOffset
, EofRec
.BeginRecord
,
592 LogFile
->Header
.EndOffset
, EofRec
.EndRecord
,
593 LogFile
->Header
.CurrentRecordNumber
, EofRec
.CurrentRecordNumber
,
594 LogFile
->Header
.OldestRecordNumber
, EofRec
.OldestRecordNumber
);
596 return STATUS_EVENTLOG_FILE_CORRUPT
;
600 /* If the log is dirty, patch the log header with the values from the EOF record */
601 if (!LogFile
->ReadOnly
&& IsLogDirty
)
603 LogFile
->Header
.StartOffset
= EofRec
.BeginRecord
;
604 LogFile
->Header
.EndOffset
= EofRec
.EndRecord
;
605 LogFile
->Header
.CurrentRecordNumber
= EofRec
.CurrentRecordNumber
;
606 LogFile
->Header
.OldestRecordNumber
= EofRec
.OldestRecordNumber
;
610 * FIXME! During operations the EOF record is the one that is the most
611 * updated (its Oldest & Current record numbers are always up-to
612 * date) while the ones from the header may be unsync. When closing
613 * (or flushing?) the event log, the header's record numbers get
614 * updated with the same values as the ones stored in the EOF record.
617 /* Verify Start/End offsets boundaries */
619 if ((LogFile
->Header
.StartOffset
>= LogFile
->CurrentSize
) ||
620 (LogFile
->Header
.StartOffset
& 3) != 0) // StartOffset % sizeof(ULONG) != 0
622 EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
623 LogFile
->Header
.StartOffset
, &LogFile
->FileName
);
624 return STATUS_EVENTLOG_FILE_CORRUPT
;
626 if ((LogFile
->Header
.EndOffset
>= LogFile
->CurrentSize
) ||
627 (LogFile
->Header
.EndOffset
& 3) != 0) // EndOffset % sizeof(ULONG) != 0
629 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
630 LogFile
->Header
.EndOffset
, &LogFile
->FileName
);
631 return STATUS_EVENTLOG_FILE_CORRUPT
;
634 if ((LogFile
->Header
.StartOffset
!= LogFile
->Header
.EndOffset
) &&
635 (LogFile
->Header
.MaxSize
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
638 * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
639 * it should point to a non-splitted EVENTLOGRECORD.
641 EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
642 LogFile
->Header
.StartOffset
, &LogFile
->FileName
);
643 return STATUS_EVENTLOG_FILE_CORRUPT
;
646 if ((LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
647 (LogFile
->Header
.EndOffset
- LogFile
->Header
.StartOffset
< sizeof(EVENTLOGRECORD
)))
650 * In non-wrapping case, there must be enough space between StartOffset
651 * and EndOffset to contain at least a full EVENTLOGRECORD.
653 EVTLTRACE("Invalid start offset 0x%x or end offset 0x%x in `%wZ'.\n",
654 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
, &LogFile
->FileName
);
655 return STATUS_EVENTLOG_FILE_CORRUPT
;
658 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
661 * Non-wrapping case: the (wrapping) free space starting at EndOffset
662 * must be able to contain an EVENTLOGEOF.
664 if (LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
+
665 LogFile
->Header
.StartOffset
- sizeof(EVENTLOGHEADER
) < sizeof(EVENTLOGEOF
))
667 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
668 LogFile
->Header
.EndOffset
, &LogFile
->FileName
);
669 return STATUS_EVENTLOG_FILE_CORRUPT
;
672 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
675 * Wrapping case: the free space between EndOffset and StartOffset
676 * must be able to contain an EVENTLOGEOF.
678 if (LogFile
->Header
.StartOffset
- LogFile
->Header
.EndOffset
< sizeof(EVENTLOGEOF
))
680 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
681 LogFile
->Header
.EndOffset
, &LogFile
->FileName
);
682 return STATUS_EVENTLOG_FILE_CORRUPT
;
686 /* Start enumerating the event records from the beginning */
687 RecOffset
= LogFile
->Header
.StartOffset
;
688 FileOffset
.QuadPart
= RecOffset
;
692 // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
694 // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
695 // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
698 DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n",
699 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
702 * For non-read-only logs of size < MaxSize, reorganize the events
703 * such that they do not wrap as soon as we write new ones.
706 if (!LogFile
->ReadOnly
)
708 pRecBuf
= LogFile
->Allocate(RecBuf
.Length
, 0, TAG_ELF_BUF
);
711 DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
720 DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n",
721 LogFile
->Header
.StartOffset
, LogFile
->Header
.EndOffset
);
724 while (FileOffset
.QuadPart
!= LogFile
->Header
.EndOffset
)
726 if (Wrapping
&& FileOffset
.QuadPart
>= RecOffset
)
728 /* We have finished enumerating all the event records */
732 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
733 Status
= LogFile
->FileRead(LogFile
,
738 if (!NT_SUCCESS(Status
))
740 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status
);
741 return STATUS_EVENTLOG_FILE_CORRUPT
;
743 if (ReadLength
!= sizeof(RecBuf
))
745 DPRINT1("Length != sizeof(RecBuf)\n");
749 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
||
750 RecBuf
.Length
< sizeof(EVENTLOGRECORD
))
752 DPRINT1("RecBuf problem\n");
756 /* Allocate a full EVENTLOGRECORD (header + data) */
757 pRecBuf
= LogFile
->Allocate(RecBuf
.Length
, 0, TAG_ELF_BUF
);
760 EVTLTRACE1("Cannot allocate heap!\n");
761 return STATUS_NO_MEMORY
;
764 /* Attempt to read the full EVENTLOGRECORD (can wrap) */
765 Status
= ReadLogBuffer(LogFile
,
771 if (!NT_SUCCESS(Status
))
773 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
774 LogFile
->Free(pRecBuf
, 0, TAG_ELF_BUF
);
775 return STATUS_EVENTLOG_FILE_CORRUPT
;
777 if (ReadLength
!= RecBuf
.Length
)
779 DPRINT1("Oh oh!!\n");
780 LogFile
->Free(pRecBuf
, 0, TAG_ELF_BUF
);
784 // /* If OverWrittenRecords is TRUE and this record has already been read */
785 // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
787 // LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
791 pRecSize2
= (PULONG
)((ULONG_PTR
)pRecBuf
+ RecBuf
.Length
- 4);
793 if (*pRecSize2
!= RecBuf
.Length
)
795 EVTLTRACE1("Invalid RecordSizeEnd of record %d (0x%x) in `%wZ'\n",
796 RecordNumber
, *pRecSize2
, &LogFile
->FileName
);
797 LogFile
->Free(pRecBuf
, 0, TAG_ELF_BUF
);
801 EVTLTRACE("Add new record %d @ offset 0x%x\n", pRecBuf
->RecordNumber
, FileOffset
.QuadPart
);
805 if (!ElfpAddOffsetInformation(LogFile
,
806 pRecBuf
->RecordNumber
,
807 FileOffset
.QuadPart
))
809 EVTLTRACE1("ElfpAddOffsetInformation() failed!\n");
810 LogFile
->Free(pRecBuf
, 0, TAG_ELF_BUF
);
811 return STATUS_EVENTLOG_FILE_CORRUPT
;
814 LogFile
->Free(pRecBuf
, 0, TAG_ELF_BUF
);
816 if (NextOffset
.QuadPart
== LogFile
->Header
.EndOffset
)
818 /* We have finished enumerating all the event records */
819 DPRINT1("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
824 * If this was the last event record before the end of the log file,
825 * the next one should start at the beginning of the log and the space
826 * between the last event record and the end of the file is padded.
828 if (LogFile
->Header
.MaxSize
- NextOffset
.QuadPart
< sizeof(EVENTLOGRECORD
))
830 /* Wrap to the beginning of the log */
832 NextOffset
.QuadPart
= sizeof(EVENTLOGHEADER
);
836 * If the next offset to read is below the current offset,
837 * this means we are wrapping.
839 if (FileOffset
.QuadPart
> NextOffset
.QuadPart
)
841 DPRINT1("Wrapping = TRUE;\n");
845 /* Move the current offset */
846 FileOffset
= NextOffset
;
849 /* If the event log was empty, it will now contain one record */
850 if (RecordNumber
!= 0 && LogFile
->Header
.OldestRecordNumber
== 0)
851 LogFile
->Header
.OldestRecordNumber
= 1;
853 LogFile
->Header
.CurrentRecordNumber
= RecordNumber
+ LogFile
->Header
.OldestRecordNumber
;
854 if (LogFile
->Header
.CurrentRecordNumber
== 0)
855 LogFile
->Header
.CurrentRecordNumber
= 1;
857 /* Flush the log if it is not read-only */
858 if (!LogFile
->ReadOnly
)
860 Status
= ElfFlushFile(LogFile
);
861 if (!NT_SUCCESS(Status
))
863 EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status
);
864 return STATUS_EVENTLOG_FILE_CORRUPT
; // Status;
868 return STATUS_SUCCESS
;
872 /* FUNCTIONS *****************************************************************/
877 IN OUT PEVTLOGFILE LogFile
,
878 IN PUNICODE_STRING FileName OPTIONAL
,
882 IN BOOLEAN CreateNew
,
884 IN PELF_ALLOCATE_ROUTINE Allocate
,
885 IN PELF_FREE_ROUTINE Free
,
886 IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize
,
887 IN PELF_FILE_WRITE_ROUTINE FileWrite
,
888 IN PELF_FILE_READ_ROUTINE FileRead
,
889 IN PELF_FILE_FLUSH_ROUTINE FileFlush
) // What about Seek ??
891 NTSTATUS Status
= STATUS_SUCCESS
;
895 /* Creating a new log file with the 'ReadOnly' flag set is incompatible */
896 if (CreateNew
&& ReadOnly
)
897 return STATUS_INVALID_PARAMETER
;
899 RtlZeroMemory(LogFile
, sizeof(*LogFile
));
901 LogFile
->Allocate
= Allocate
;
902 LogFile
->Free
= Free
;
903 LogFile
->FileSetSize
= FileSetSize
;
904 LogFile
->FileWrite
= FileWrite
;
905 LogFile
->FileRead
= FileRead
;
906 LogFile
->FileFlush
= FileFlush
;
908 /* Copy the log file name if provided (optional) */
909 RtlInitEmptyUnicodeString(&LogFile
->FileName
, NULL
, 0);
910 if (FileName
&& FileName
->Buffer
&& FileName
->Length
&&
911 (FileName
->Length
<= FileName
->MaximumLength
))
913 LogFile
->FileName
.Buffer
= LogFile
->Allocate(FileName
->Length
,
916 if (LogFile
->FileName
.Buffer
)
918 LogFile
->FileName
.MaximumLength
= FileName
->Length
;
919 RtlCopyUnicodeString(&LogFile
->FileName
, FileName
);
923 LogFile
->OffsetInfo
= LogFile
->Allocate(OFFSET_INFO_INCREMENT
* sizeof(EVENT_OFFSET_INFO
),
926 if (LogFile
->OffsetInfo
== NULL
)
928 EVTLTRACE1("Cannot allocate heap\n");
929 Status
= STATUS_NO_MEMORY
;
932 LogFile
->OffsetInfoSize
= OFFSET_INFO_INCREMENT
;
933 LogFile
->OffsetInfoNext
= 0;
935 // FIXME: Always use the regitry values for MaxSize,
936 // even for existing logs!
938 // FIXME: On Windows, EventLog uses the MaxSize setting
939 // from the registry itself; the MaxSize from the header
940 // is just for information purposes.
942 EVTLTRACE("Initializing log file `%wZ'\n", &LogFile
->FileName
);
944 LogFile
->ReadOnly
= ReadOnly
; // !CreateNew && ReadOnly;
947 Status
= ElfpInitNewFile(LogFile
, FileSize
, MaxSize
, Retention
);
949 Status
= ElfpInitExistingFile(LogFile
, FileSize
, /* MaxSize, */ Retention
);
952 if (!NT_SUCCESS(Status
))
954 if (LogFile
->OffsetInfo
)
955 LogFile
->Free(LogFile
->OffsetInfo
, 0, TAG_ELF
);
957 if (LogFile
->FileName
.Buffer
)
958 LogFile
->Free(LogFile
->FileName
.Buffer
, 0, TAG_ELF
);
967 IN PEVTLOGFILE LogFile
)
971 return ElfpInitNewFile(LogFile
,
972 LogFile
->CurrentSize
,
973 LogFile
->Header
.MaxSize
,
974 LogFile
->Header
.Retention
);
980 IN PEVTLOGFILE LogFile
,
981 IN PEVTLOGFILE BackupLogFile
)
985 LARGE_INTEGER FileOffset
;
986 SIZE_T ReadLength
, WrittenLength
;
987 PEVENTLOGHEADER Header
;
988 EVENTLOGRECORD RecBuf
;
996 RtlZeroMemory(BackupLogFile
, sizeof(*BackupLogFile
));
998 BackupLogFile
->FileSetSize
= LogFile
->FileSetSize
;
999 BackupLogFile
->FileWrite
= LogFile
->FileWrite
;
1000 BackupLogFile
->FileFlush
= LogFile
->FileFlush
;
1002 // BackupLogFile->CurrentSize = LogFile->CurrentSize;
1004 BackupLogFile
->ReadOnly
= FALSE
;
1006 /* Initialize the (dirty) log file header */
1007 Header
= &BackupLogFile
->Header
;
1008 Header
->HeaderSize
= sizeof(EVENTLOGHEADER
);
1009 Header
->Signature
= LOGFILE_SIGNATURE
;
1010 Header
->MajorVersion
= MAJORVER
;
1011 Header
->MinorVersion
= MINORVER
;
1012 Header
->StartOffset
= sizeof(EVENTLOGHEADER
);
1013 Header
->EndOffset
= sizeof(EVENTLOGHEADER
);
1014 Header
->CurrentRecordNumber
= 1;
1015 Header
->OldestRecordNumber
= 0;
1016 Header
->MaxSize
= LogFile
->Header
.MaxSize
;
1017 Header
->Flags
= ELF_LOGFILE_HEADER_DIRTY
;
1018 Header
->Retention
= LogFile
->Header
.Retention
;
1019 Header
->EndHeaderSize
= sizeof(EVENTLOGHEADER
);
1021 /* Write the (dirty) log file header */
1022 FileOffset
.QuadPart
= 0LL;
1023 Status
= BackupLogFile
->FileWrite(BackupLogFile
,
1026 sizeof(EVENTLOGHEADER
),
1028 if (!NT_SUCCESS(Status
))
1030 EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status
);
1034 for (i
= LogFile
->Header
.OldestRecordNumber
; i
< LogFile
->Header
.CurrentRecordNumber
; i
++)
1036 RecOffset
= ElfpOffsetByNumber(LogFile
, i
);
1040 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
1041 FileOffset
.QuadPart
= RecOffset
;
1042 Status
= LogFile
->FileRead(LogFile
,
1047 if (!NT_SUCCESS(Status
))
1049 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status
);
1053 // if (ReadLength != sizeof(RecBuf))
1056 Buffer
= LogFile
->Allocate(RecBuf
.Length
, 0, TAG_ELF_BUF
);
1059 EVTLTRACE1("Allocate() failed!\n");
1063 /* Read the full EVENTLOGRECORD (header + data) with wrapping */
1064 Status
= ReadLogBuffer(LogFile
,
1070 if (!NT_SUCCESS(Status
))
1072 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1073 LogFile
->Free(Buffer
, 0, TAG_ELF_BUF
);
1074 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1078 /* Write the event record (no wrap for the backup log) */
1079 Status
= BackupLogFile
->FileWrite(BackupLogFile
,
1084 if (!NT_SUCCESS(Status
))
1086 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
1087 LogFile
->Free(Buffer
, 0, TAG_ELF_BUF
);
1091 /* Update the header information */
1092 Header
->EndOffset
+= RecBuf
.Length
;
1094 /* Free the buffer */
1095 LogFile
->Free(Buffer
, 0, TAG_ELF_BUF
);
1101 /* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */
1102 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
1103 EofRec
.BeginRecord
= Header
->StartOffset
;
1104 EofRec
.EndRecord
= Header
->EndOffset
;
1105 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1106 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1108 Status
= BackupLogFile
->FileWrite(BackupLogFile
,
1113 if (!NT_SUCCESS(Status
))
1115 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
1119 /* Update the header information */
1120 Header
->CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1121 Header
->OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1122 Header
->MaxSize
= ROUND_UP(Header
->EndOffset
+ sizeof(EofRec
), sizeof(ULONG
));
1123 Header
->Flags
= 0; // FIXME?
1125 /* Flush the log file - Write the (clean) log file header */
1126 Status
= ElfFlushFile(BackupLogFile
);
1135 IN PEVTLOGFILE LogFile
)
1138 LARGE_INTEGER FileOffset
;
1139 SIZE_T WrittenLength
;
1143 if (LogFile
->ReadOnly
)
1144 return STATUS_SUCCESS
; // STATUS_ACCESS_DENIED;
1147 * NOTE that both the EOF record *AND* the log file header
1148 * are supposed to be already updated!
1149 * We just remove the dirty log bit.
1151 LogFile
->Header
.Flags
&= ~ELF_LOGFILE_HEADER_DIRTY
;
1153 /* Update the log file header */
1154 FileOffset
.QuadPart
= 0LL;
1155 Status
= LogFile
->FileWrite(LogFile
,
1158 sizeof(EVENTLOGHEADER
),
1160 if (!NT_SUCCESS(Status
))
1162 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
1166 /* Flush the log file */
1167 Status
= LogFile
->FileFlush(LogFile
, NULL
, 0);
1168 if (!NT_SUCCESS(Status
))
1170 EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status
);
1174 return STATUS_SUCCESS
;
1179 ElfCloseFile( // ElfFree
1180 IN PEVTLOGFILE LogFile
)
1184 /* Flush the log file */
1185 ElfFlushFile(LogFile
);
1188 LogFile
->Free(LogFile
->OffsetInfo
, 0, TAG_ELF
);
1190 if (LogFile
->FileName
.Buffer
)
1191 LogFile
->Free(LogFile
->FileName
.Buffer
, 0, TAG_ELF
);
1192 RtlInitEmptyUnicodeString(&LogFile
->FileName
, NULL
, 0);
1198 IN PEVTLOGFILE LogFile
,
1199 IN ULONG RecordNumber
,
1200 OUT PEVENTLOGRECORD Record
,
1201 IN SIZE_T BufSize
, // Length
1202 OUT PSIZE_T BytesRead OPTIONAL
,
1203 OUT PSIZE_T BytesNeeded OPTIONAL
)
1206 LARGE_INTEGER FileOffset
;
1219 /* Retrieve the offset of the event record */
1220 RecOffset
= ElfpOffsetByNumber(LogFile
, RecordNumber
);
1222 return STATUS_NOT_FOUND
;
1224 /* Retrieve its full size */
1225 FileOffset
.QuadPart
= RecOffset
;
1226 Status
= LogFile
->FileRead(LogFile
,
1231 if (!NT_SUCCESS(Status
))
1233 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status
);
1234 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1238 /* Check whether the buffer is big enough to hold the event record */
1239 if (BufSize
< RecSize
)
1242 *BytesNeeded
= RecSize
;
1244 return STATUS_BUFFER_TOO_SMALL
;
1247 /* Read the event record into the buffer */
1248 FileOffset
.QuadPart
= RecOffset
;
1249 Status
= ReadLogBuffer(LogFile
,
1255 if (!NT_SUCCESS(Status
))
1257 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status
);
1258 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1262 *BytesRead
= ReadLength
;
1270 IN PEVTLOGFILE LogFile
,
1271 IN PEVENTLOGRECORD Record
,
1275 LARGE_INTEGER FileOffset
, NextOffset
;
1276 SIZE_T ReadLength
, WrittenLength
;
1278 EVENTLOGRECORD RecBuf
;
1279 ULONG FreeSpace
= 0;
1281 ULONG RecOffset
, WriteOffset
;
1285 if (LogFile
->ReadOnly
)
1286 return STATUS_ACCESS_DENIED
;
1288 // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1290 if (!Record
|| BufSize
< sizeof(*Record
))
1291 return STATUS_INVALID_PARAMETER
;
1293 Record
->RecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1295 /* Compute the available log free space */
1296 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
1297 FreeSpace
= LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
+ LogFile
->Header
.StartOffset
- sizeof(EVENTLOGHEADER
);
1298 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1299 FreeSpace
= LogFile
->Header
.StartOffset
- LogFile
->Header
.EndOffset
;
1301 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_DIRTY
;
1303 /* If the event log was empty, it will now contain one record */
1304 if (LogFile
->Header
.OldestRecordNumber
== 0)
1305 LogFile
->Header
.OldestRecordNumber
= 1;
1307 /* By default we append the new record at the old EOF record offset */
1308 WriteOffset
= LogFile
->Header
.EndOffset
;
1311 * Check whether the log is going to wrap (the events being overwritten).
1314 if (LogFile
->Header
.StartOffset
<= LogFile
->Header
.EndOffset
)
1315 UpperBound
= LogFile
->Header
.MaxSize
;
1316 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1317 UpperBound
= LogFile
->Header
.StartOffset
;
1319 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1320 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
1322 EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n"
1323 "UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n",
1324 LogFile
->Header
.MaxSize
, UpperBound
, WriteOffset
, BufSize
);
1325 /* This will be done later */
1328 if ( (LogFile
->Header
.StartOffset
< LogFile
->Header
.EndOffset
) &&
1329 (LogFile
->Header
.MaxSize
- WriteOffset
< sizeof(RecBuf
)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1331 // ASSERT(UpperBound == LogFile->Header.MaxSize);
1332 // ASSERT(WriteOffset == LogFile->Header.EndOffset);
1335 * We cannot fit the EVENTLOGRECORD header of the buffer before
1336 * the end of the file. We need to pad the end of the log with
1337 * 0x00000027, normally we will need to pad at most 0x37 bytes
1338 * (corresponding to sizeof(EVENTLOGRECORD) - 1).
1341 /* Rewind to the beginning of the log, just after the header */
1342 WriteOffset
= sizeof(EVENTLOGHEADER
);
1343 /**/UpperBound
= LogFile
->Header
.StartOffset
;/**/
1345 FreeSpace
= LogFile
->Header
.StartOffset
- WriteOffset
;
1347 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1350 * Otherwise, we can fit the header and only part
1351 * of the data will overwrite the oldest records.
1353 * It might be possible that all the event record can fit in one piece,
1354 * but that the EOF record needs to be split. This is not a problem,
1355 * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
1358 if (UpperBound
- WriteOffset
< BufSize
+ sizeof(EofRec
))
1360 ULONG OrgOldestRecordNumber
, OldestRecordNumber
;
1362 // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1364 OldestRecordNumber
= OrgOldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1366 // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
1367 // NOTE: It should be, by construction (and this should have been checked when
1368 // initializing a new, or existing log).
1371 * Determine how many old records need to be overwritten.
1372 * Check the size of the record as the record added may be larger.
1373 * Need to take into account that we append the EOF record.
1375 while (FreeSpace
< BufSize
+ sizeof(EofRec
))
1377 /* Get the oldest record data */
1378 RecOffset
= ElfpOffsetByNumber(LogFile
, OldestRecordNumber
);
1381 EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber
);
1382 LogFile
->Header
.Flags
|= ELF_LOGFILE_LOGFULL_WRITTEN
;
1383 return STATUS_LOG_FILE_FULL
;
1386 RtlZeroMemory(&RecBuf
, sizeof(RecBuf
));
1388 FileOffset
.QuadPart
= RecOffset
;
1389 Status
= LogFile
->FileRead(LogFile
,
1394 if (!NT_SUCCESS(Status
))
1396 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status
);
1397 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1401 if (RecBuf
.Reserved
!= LOGFILE_SIGNATURE
)
1403 EVTLTRACE1("The event log file is corrupted!\n");
1404 return STATUS_EVENTLOG_FILE_CORRUPT
;
1408 * Check whether this event can be overwritten by comparing its
1409 * written timestamp with the log's retention value. This value
1410 * is the time interval, in seconds, that events records are
1411 * protected from being overwritten.
1413 * If the retention value is zero the events are always overwritten.
1415 * If the retention value is non-zero, when the age of an event,
1416 * in seconds, reaches or exceeds this value, it can be overwritten.
1417 * Also if the events are in the future, we do not overwrite them.
1419 if (LogFile
->Header
.Retention
!= 0 &&
1420 (Record
->TimeWritten
< RecBuf
.TimeWritten
||
1421 (Record
->TimeWritten
>= RecBuf
.TimeWritten
&&
1422 Record
->TimeWritten
- RecBuf
.TimeWritten
< LogFile
->Header
.Retention
)))
1424 EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n");
1425 LogFile
->Header
.Flags
|= ELF_LOGFILE_LOGFULL_WRITTEN
;
1426 return STATUS_LOG_FILE_FULL
;
1430 * Advance the oldest record number, add the event record length
1431 * (as long as it is valid...) then take account for the possible
1432 * paddind after the record, in case this is the last one at the
1435 OldestRecordNumber
++;
1436 RecOffset
+= RecBuf
.Length
;
1437 FreeSpace
+= RecBuf
.Length
;
1440 * If this was the last event record before the end of the log file,
1441 * the next one should start at the beginning of the log and the space
1442 * between the last event record and the end of the file is padded.
1444 if (LogFile
->Header
.MaxSize
- RecOffset
< sizeof(EVENTLOGRECORD
))
1446 /* Add the padding size */
1447 FreeSpace
+= LogFile
->Header
.MaxSize
- RecOffset
;
1451 EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace
, BufSize
);
1453 /* The log records are wrapping */
1454 LogFile
->Header
.Flags
|= ELF_LOGFILE_HEADER_WRAP
;
1457 // FIXME: May lead to corruption if the other subsequent calls fail...
1460 * We have validated all the region of events to be discarded,
1461 * now we can perform their deletion.
1463 ElfpDeleteOffsetInformation(LogFile
, OrgOldestRecordNumber
, OldestRecordNumber
- 1);
1464 LogFile
->Header
.OldestRecordNumber
= OldestRecordNumber
;
1465 LogFile
->Header
.StartOffset
= ElfpOffsetByNumber(LogFile
, OldestRecordNumber
);
1466 if (LogFile
->Header
.StartOffset
== 0)
1469 * We have deleted all the existing event records to make place
1470 * for the new one. We can put it at the start of the event log.
1472 LogFile
->Header
.StartOffset
= sizeof(EVENTLOGHEADER
);
1473 WriteOffset
= LogFile
->Header
.StartOffset
;
1474 LogFile
->Header
.EndOffset
= WriteOffset
;
1477 DPRINT1("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n"
1478 "OldestRecordNumber = %d\n",
1479 LogFile
->Header
.MaxSize
, LogFile
->Header
.StartOffset
, WriteOffset
, LogFile
->Header
.EndOffset
, BufSize
,
1480 OldestRecordNumber
);
1484 * Expand the log file if needed.
1485 * NOTE: It may be needed to perform this task a bit sooner if we need
1486 * such a thing for performing read operations, in the future...
1487 * Or if this operation needs to modify 'FreeSpace'...
1489 if (LogFile
->CurrentSize
< LogFile
->Header
.MaxSize
)
1491 EVTLTRACE1("Expanding the log file from %lu to %lu\n",
1492 LogFile
->CurrentSize
, LogFile
->Header
.MaxSize
);
1494 LogFile
->CurrentSize
= LogFile
->Header
.MaxSize
;
1495 LogFile
->FileSetSize(LogFile
, LogFile
->CurrentSize
, 0);
1498 /* Since we can write events in the log, clear the log full flag */
1499 LogFile
->Header
.Flags
&= ~ELF_LOGFILE_LOGFULL_WRITTEN
;
1501 /* Pad the end of the log */
1502 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1503 if (WriteOffset
< LogFile
->Header
.EndOffset
)
1505 /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
1506 WrittenLength
= ROUND_DOWN(LogFile
->Header
.MaxSize
- LogFile
->Header
.EndOffset
, sizeof(ULONG
));
1507 RtlFillMemoryUlong(&RecBuf
, WrittenLength
, 0x00000027);
1509 FileOffset
.QuadPart
= LogFile
->Header
.EndOffset
;
1510 Status
= LogFile
->FileWrite(LogFile
,
1515 if (!NT_SUCCESS(Status
))
1517 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status
);
1522 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1523 FileOffset
.QuadPart
= WriteOffset
;
1524 Status
= WriteLogBuffer(LogFile
,
1530 if (!NT_SUCCESS(Status
))
1532 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1535 /* FileOffset now contains the offset just after the end of the record buffer */
1536 FileOffset
= NextOffset
;
1538 if (!ElfpAddOffsetInformation(LogFile
,
1539 Record
->RecordNumber
,
1542 return STATUS_NO_MEMORY
; // STATUS_EVENTLOG_FILE_CORRUPT;
1545 LogFile
->Header
.CurrentRecordNumber
++;
1546 if (LogFile
->Header
.CurrentRecordNumber
== 0)
1547 LogFile
->Header
.CurrentRecordNumber
= 1;
1550 * Write the new EOF record offset just after the event record.
1551 * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
1552 * bytes remains between the end of the record and the end of the log file.
1554 LogFile
->Header
.EndOffset
= FileOffset
.QuadPart
;
1556 RtlCopyMemory(&EofRec
, &EOFRecord
, sizeof(EOFRecord
));
1557 EofRec
.BeginRecord
= LogFile
->Header
.StartOffset
;
1558 EofRec
.EndRecord
= LogFile
->Header
.EndOffset
;
1559 EofRec
.CurrentRecordNumber
= LogFile
->Header
.CurrentRecordNumber
;
1560 EofRec
.OldestRecordNumber
= LogFile
->Header
.OldestRecordNumber
;
1562 // FileOffset.QuadPart = LogFile->Header.EndOffset;
1563 Status
= WriteLogBuffer(LogFile
,
1569 if (!NT_SUCCESS(Status
))
1571 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status
);
1574 FileOffset
= NextOffset
;
1576 /* Flush the log file */
1577 Status
= ElfFlushFile(LogFile
);
1578 if (!NT_SUCCESS(Status
))
1580 EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status
);
1581 return STATUS_EVENTLOG_FILE_CORRUPT
; // Status;
1590 IN PEVTLOGFILE LogFile
)
1593 return LogFile
->Header
.OldestRecordNumber
;
1598 ElfGetCurrentRecord(
1599 IN PEVTLOGFILE LogFile
)
1602 return LogFile
->Header
.CurrentRecordNumber
;
1608 IN PEVTLOGFILE LogFile
)
1611 return LogFile
->Header
.Flags
;
1615 VOID
PRINT_HEADER(PEVENTLOGHEADER Header
)
1617 ULONG Flags
= Header
->Flags
;
1619 EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header
);
1621 DbgPrint("HeaderSize = %lu\n" , Header
->HeaderSize
);
1622 DbgPrint("Signature = 0x%x\n", Header
->Signature
);
1623 DbgPrint("MajorVersion = %lu\n" , Header
->MajorVersion
);
1624 DbgPrint("MinorVersion = %lu\n" , Header
->MinorVersion
);
1625 DbgPrint("StartOffset = 0x%x\n", Header
->StartOffset
);
1626 DbgPrint("EndOffset = 0x%x\n", Header
->EndOffset
);
1627 DbgPrint("CurrentRecordNumber = %lu\n", Header
->CurrentRecordNumber
);
1628 DbgPrint("OldestRecordNumber = %lu\n", Header
->OldestRecordNumber
);
1629 DbgPrint("MaxSize = 0x%x\n", Header
->MaxSize
);
1630 DbgPrint("Retention = 0x%x\n", Header
->Retention
);
1631 DbgPrint("EndHeaderSize = %lu\n" , Header
->EndHeaderSize
);
1632 DbgPrint("Flags: ");
1633 if (Flags
& ELF_LOGFILE_HEADER_DIRTY
)
1635 DbgPrint("ELF_LOGFILE_HEADER_DIRTY");
1636 Flags
&= ~ELF_LOGFILE_HEADER_DIRTY
;
1638 if (Flags
) DbgPrint(" | ");
1639 if (Flags
& ELF_LOGFILE_HEADER_WRAP
)
1641 DbgPrint("ELF_LOGFILE_HEADER_WRAP");
1642 Flags
&= ~ELF_LOGFILE_HEADER_WRAP
;
1644 if (Flags
) DbgPrint(" | ");
1645 if (Flags
& ELF_LOGFILE_LOGFULL_WRITTEN
)
1647 DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN");
1648 Flags
&= ~ELF_LOGFILE_LOGFULL_WRITTEN
;
1650 if (Flags
) DbgPrint(" | ");
1651 if (Flags
& ELF_LOGFILE_ARCHIVE_SET
)
1653 DbgPrint("ELF_LOGFILE_ARCHIVE_SET");
1654 Flags
&= ~ELF_LOGFILE_ARCHIVE_SET
;
1656 if (Flags
) DbgPrint(" | 0x%x", Flags
);